/*++

Copyright (c) 2004 - 2007, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  VfrCompile.g

Abstract:

  PCCTS parser and lexer definitions for the EFI VFR forms compiler
  
--*/  

#header<<

#ifndef INT8_defined
#define INT8_defined
typedef char INT8;
#endif

#define _stricmp strcasecmp

#include "Tiano.h"
#include "EfiUtilityMsgs.h"
#include "EfiVfr.h"
#include "VfrServices.h"
#include EFI_PROTOCOL_DEFINITION (Hii)

#include <ctype.h>
#include <unistd.h>  // for getcwd()
#include <stdlib.h> // for system() spawn functions

>>

<<

//
// Base info for DLG-generated scanner
//
#include "DLexerBase.h"    

//
// Include the scanner file generated by DLG
//
#include "DLGLexer.h"    

class DLGLexerVfr : public DLGLexer
{
public:
  DLGLexerVfr (DLGFileInput *F) : DLGLexer (F) {};
  INT32 errstd (char *Text) 
  { 
    printf ("unrecognized input '%s'\n", Text); 
  }
};

//
// Base token definitions for ANTLR
//
#include "AToken.h"

//
// This is how we invoke the C preprocessor on the VFR source file
// to resolve #defines, #includes, etc. To make C source files
// shareable between VFR and drivers, define VFRCOMPILE so that
// #ifdefs can be used in shared .h files.
//
#define PREPROCESSOR_COMMAND        "gcc "
#define PREPROCESSOR_OPTIONS        "-E -P -x c -D VFRCOMPILE "

typedef ANTLRCommonToken ANTLRToken;

//
// Specify the filename extensions for the files we generate.
//
#define VFR_BINARY_FILENAME_EXTENSION       ".c"
#define VFR_LIST_FILENAME_EXTENSION         ".lst"
#define VFR_PREPROCESS_FILENAME_EXTENSION   ".i"

static 
VOID 
Usage ();

static 
STATUS 
ProcessArgs (
  int         Argc, 
  char        *Argv[]
  );

static 
VOID 
Cleanup ();

//
// Globals
//
OPTIONS gOptions;

int 
main (
  int   argc, 
  char  **argv
  )
/*++

Routine Description:
  Application entry point function. Parse command-line arguments, 
  invoke the parser, clean up, and return.

Arguments:
  argc - standard argc passed to main() per C conventions
  argv - standard argv passed to main() per C conventions

Returns:
  STATUS_SUCCESS - program executed with no errors or warnings
  STATUS_WARNING - program executed with warnings
  STATUS_ERROR   - non-recoverable errors encountered while processing

--*/
{
  FILE      *VfrFptr;
  char      *Cmd;
  char      *Cptr;
  int       Len;
  STATUS    Status;
    
  //
  // Set our program name for the error printing routines.
  // Then set printing limits.
  //
  SetUtilityName (PROGRAM_NAME);
  SetPrintLimits (20, 20, 30);
  //
  // Process the command-line arguments
  //
  if (ProcessArgs (argc, argv) != STATUS_SUCCESS) {
    Usage ();
    Cleanup();
    return STATUS_ERROR;
  }
  VfrFptr = NULL;
  //
  // Verify the VFR script file exists
  //
  if ((VfrFptr = fopen (gOptions.VfrFileName, "r")) == NULL) {
    Error (PROGRAM_NAME, 0, 0, gOptions.VfrFileName, "could not open input VFR file");
    Cleanup();
    return STATUS_ERROR;
  }
  //
  // Now close the file and make a system call to run the preprocessor
  // on it.
  //
  fclose (VfrFptr);
  Len = strlen (PREPROCESSOR_OPTIONS) + strlen (gOptions.VfrFileName) + 10 +
        strlen (PREPROCESSOR_COMMAND) + strlen (gOptions.PreprocessorOutputFileName);
  if (gOptions.CPreprocessorOptions != NULL) {
    Len += strlen (gOptions.CPreprocessorOptions) + 1;
  }
  if (gOptions.IncludePaths != NULL) {
    Len += strlen (gOptions.IncludePaths) + 1;
  }
  Cmd = (char *)malloc (Len);
  if (Cmd == NULL) {
    Error (PROGRAM_NAME, 0, 0, NULL, "could not allocate memory");
    Cleanup();
    return STATUS_ERROR;
  }  
  strcpy (Cmd, PREPROCESSOR_COMMAND PREPROCESSOR_OPTIONS);
  if (gOptions.IncludePaths != NULL) {
    strcat (Cmd, gOptions.IncludePaths);
    strcat (Cmd, " ");
  }
  if (gOptions.CPreprocessorOptions != NULL) {
    strcat (Cmd, gOptions.CPreprocessorOptions);
    strcat (Cmd, " ");
  }
  strcat (Cmd, gOptions.VfrFileName);
  strcat (Cmd, " > ");
  strcat (Cmd, gOptions.PreprocessorOutputFileName);
  Status = system (Cmd);
  if (Status != 0) {
    Error (PROGRAM_NAME, 0, 0, gOptions.VfrFileName, "failed to spawn C preprocessor on VFR file");
    printf ("Command: '%s %s'\n", PREPROCESSOR_COMMAND, Cmd);
    Cleanup();
    return STATUS_ERROR;
  }
  free (Cmd);
  //
  // Open the preprocessor output file
  //
  if ((VfrFptr = fopen (gOptions.PreprocessorOutputFileName, "r")) == NULL) {
    Error (PROGRAM_NAME, 0, 0, "failed to open input VFR preprocessor output file", 
      gOptions.PreprocessorOutputFileName);
    Cleanup();
    return STATUS_ERROR;
  }
  //
  // Define input VFR file
  //
  DLGFileInput InputFile (VfrFptr);
  //
  // Define an instance of the scanner    
  //
  DLGLexerVfr Scanner (&InputFile);
  //
  // Define token buffer between scanner and parser
  //
  ANTLRTokenBuffer Pipe (&Scanner);    
  //
  // Create a token to use as a model
  //
  ANTLRToken Tok;     
  //
  // Tell the scanner what type the token is
  //
  Scanner.setToken (&Tok);    
  //
  // Create an instance of our parser
  //
  EfiVfrParser Parser (&Pipe);    
  //
  // Initialize the parser    
  //
  Parser.init ();
  Status = GetUtilityStatus ();
  if (Status != STATUS_SUCCESS) {
    Cleanup();
    return Status;
  }  
  //
  // Start the first rule    
  //
  Parser.program ();
  //
  // Close the input script file
  //
  fclose (VfrFptr);
  Parser.WriteIfrBytes ();
  //
  // Call cleanup, which does some extra checking of the script
  //
  Parser.Cleanup ();
  Cleanup();
  //
  // If we had an error somewhere, delete our output files so that
  // a subsequent build will rebuild them.
  //
  Status = GetUtilityStatus ();
  if (Status == STATUS_ERROR) {
    remove (gOptions.IfrOutputFileName);
  }
  return Status;
}
static
VOID
Cleanup ()
/*++

Routine Description:
  Free up memory allocated during parsing.

Arguments:
  None

Returns:
  None

--*/
{
  //
  // Free up our string we allocated to track the include paths
  //
  if (gOptions.IncludePaths != NULL) {
    free (gOptions.IncludePaths);
    gOptions.IncludePaths = NULL;
  }
  //
  // Free up our string we allocated to track preprocessor options
  //
  if (gOptions.CPreprocessorOptions != NULL) {
    free (gOptions.CPreprocessorOptions);
    gOptions.CPreprocessorOptions = NULL;
  }
}  

static
STATUS
ProcessArgs (
  int         Argc, 
  char        *Argv[]
  )
/*++

Routine Description:
  Process the command-line arguments.

Arguments:
  Argc - standard argc passed to main()
  Argv - standard argv passed to main()

Returns:
  STATUS_SUCCESS - program should continue (all args ok)

--*/
{
  char    *IncludePaths;
  char    *CPreprocessorOptions;
  int     Len;  
  char    CopyStr[MAX_PATH];
  char    *Cptr;

  //
  // Put options in known state.
  //
  memset ((char *)&gOptions, 0, sizeof (OPTIONS));
  //
  // Go through all the arguments that start with '-'
  //
  Argc--;
  Argv++;
  while ((Argc > 0) && (Argv[0][0] == '-')) {
    //
    // -? or -h help option -- return an error for printing usage
    //
    if ((_stricmp (Argv[0], "-?") == 0) || (_stricmp (Argv[0], "-h") == 0)) {
      return STATUS_ERROR;
      break;
    //
    // -l to create a listing output file
    //
    } else if (_stricmp (Argv[0], "-l") == 0) {
      gOptions.CreateListFile = 1;
    //
    // -I include_path option for finding include files. We'll pass this
    // to the preprocessor. Turn them all into a single include string.
    //
    } else if (_stricmp (Argv[0], "-i") == 0) {
      if ((Argc < 2) || (Argv[1][0] == '-')) {
        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing path argument");
        return STATUS_ERROR;
      }
      Argc--;
      Argv++;
      Len = strlen (" -I ");
      Len += strlen (Argv[0]) + 2;
      if (gOptions.IncludePaths != NULL) {
        Len += strlen (gOptions.IncludePaths);
      }
      IncludePaths = (INT8 *)malloc (Len);
      if (IncludePaths == NULL) {
        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");
        return STATUS_ERROR;
      }
      IncludePaths[0] = 0;
      if (gOptions.IncludePaths != NULL) {
        strcpy (IncludePaths, gOptions.IncludePaths);
        free (gOptions.IncludePaths);
      }
      strcat (IncludePaths, " -I ");
      strcat (IncludePaths, Argv[0]);
      gOptions.IncludePaths = IncludePaths;
    //
    // -od OutputDirectory to define a common directory for output files
    //
    } else if (_stricmp (Argv[0], "-od") == 0) {
      if ((Argc < 2) || (Argv[1][0] == '-')) {
        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing output directory name");
        return STATUS_ERROR;
      }
      Argc--;
      Argv++;
      strcpy (gOptions.OutputDirectory, Argv[0]);
    } else if (_stricmp (Argv[0], "-ibin") == 0) {
      gOptions.CreateIfrBinFile = 1;
    } else if (_stricmp (Argv[0], "-nostrings") == 0) {
      // deprecated option
    //
    // -ppflag C-preprocessor-flag option for passing options to the C preprocessor.
    // Turn them all into a single string.
    //
    } else if (_stricmp (Argv[0], "-ppflag") == 0) {
      if (Argc < 2) {
        Error (PROGRAM_NAME, 0, 0, Argv[0], "missing C-preprocessor argument");
        return STATUS_ERROR;
      }
      Argc--;
      Argv++;
      Len = strlen (Argv[0]) + 2;
      if (gOptions.CPreprocessorOptions != NULL) {
        Len += strlen (gOptions.CPreprocessorOptions);
      }
      CPreprocessorOptions = (INT8 *)malloc (Len);
      if (CPreprocessorOptions == NULL) {
        Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");
        return STATUS_ERROR;
      }
      CPreprocessorOptions[0] = 0;
      if (gOptions.CPreprocessorOptions != NULL) {
        strcpy (CPreprocessorOptions, gOptions.CPreprocessorOptions);
        free (gOptions.CPreprocessorOptions);
      }
      strcat (CPreprocessorOptions, " ");
      strcat (CPreprocessorOptions, Argv[0]);
      gOptions.CPreprocessorOptions = CPreprocessorOptions;
    } else {
      Error (PROGRAM_NAME, 0, 0, Argv[0], "unrecognized option");
      return STATUS_ERROR;
    }
    Argc--;
    Argv++;
  }
  //
  // Must specify at least the vfr file name
  //
  if (Argc > 1) {
    Error (PROGRAM_NAME, 0, 0, Argv[1], "unrecognized argument after VFR file name");
    return STATUS_ERROR;
  } else if (Argc < 1) {
    Error (PROGRAM_NAME, 0, 0, NULL, "must specify VFR file name");
    return STATUS_ERROR;
  }
  strcpy (gOptions.VfrFileName, Argv[0]);
  
  strcpy (CopyStr, gOptions.VfrFileName);
  Cptr = CopyStr + strlen (CopyStr) - 1;
  for (;(Cptr > CopyStr) && (*Cptr != '/') ; Cptr--);
  if (Cptr == CopyStr) {
    strcpy (gOptions.VfrBaseFileName, Cptr);
  } else {
    strcpy (gOptions.VfrBaseFileName, Cptr+1);
  }
  //
  // Terminate the vfr file basename at the extension
  //
  for (Cptr = gOptions.VfrBaseFileName; *Cptr && (*Cptr != '.'); Cptr++) {
  }
  *Cptr = 0; 
  //
  // If they defined an output directory, prepend all output files
  // with the working directory. Output files of interest:
  //    VfrListFileName             -- list file
  //    IfrOutputFileName           -- IFR bytes 
  //    StringOutputFileName        -- string bytes
  //    StringListFileName          -- not used
  //    StringDefineFileName        -- #defines of string identifiers
  //
  // We have two cases:
  //   1. Output directory (-od) not specified, in which case output files
  //      go to the current working directory.
  //   2. Output directory specified, in which case the output files
  //      go directly to the specified directory.
  //
  if (gOptions.OutputDirectory[0] == 0) {
    CopyStr[0] = 0;
    getcwd (CopyStr, sizeof (CopyStr));
    strcpy (gOptions.OutputDirectory, CopyStr);
  }
  //
  // Make sure output directory has a trailing backslash
  //
  if (gOptions.OutputDirectory[strlen (gOptions.OutputDirectory) - 1] != '/') {
    strcat (gOptions.OutputDirectory, "/");
  }
  //
  // Create the base output file name as: path\base, copy it to all the output
  // filenames, and then add the appropriate extension to each.
  //
  strcpy (gOptions.VfrListFileName, gOptions.OutputDirectory);
  strcat (gOptions.VfrListFileName, gOptions.VfrBaseFileName);
  strcpy (gOptions.IfrOutputFileName, gOptions.VfrListFileName);
  strcpy (gOptions.PreprocessorOutputFileName, gOptions.VfrListFileName);
  strcat (gOptions.VfrListFileName, VFR_LIST_FILENAME_EXTENSION);
  strcat (gOptions.IfrOutputFileName, VFR_BINARY_FILENAME_EXTENSION);
  strcat (gOptions.PreprocessorOutputFileName, VFR_PREPROCESS_FILENAME_EXTENSION);
  
  //
  // We set a default list file name, so if they do not
  // want a list file, null out the name now.
  //
  if (gOptions.CreateListFile == 0) {
    gOptions.VfrListFileName[0] = 0;
  }
  return STATUS_SUCCESS;
}
static 
VOID 
Usage ()
/*++

Routine Description:
  Print utility usage instructions

Arguments:
  None

Returns:
  None

--*/
{
  int Index;
  const char *Help[] = {
    " ", 
    "VfrCompile version " VFR_COMPILER_VERSION,
    " ",
    "  Usage: VfrCompile {options} [VfrFile]",
    " ",
    "    where options include:",
    "      -? or -h       prints this help",
    "      -l             create an output IFR listing file",
    "      -i IncPath     add IncPath to the search path for VFR included files",
    "      -od OutputDir  deposit all output files to directory OutputDir (default=cwd)",
    "      -ibin          create an IFR HII pack file",
    "    where parameters include:",
    "      VfrFile        name of the input VFR script file",
    " ",
    NULL
    };
  for (Index = 0; Help[Index] != NULL; Index++) {
    fprintf (stdout, "%s\n", Help[Index]);
  }
}
    
>>


#lexaction
<<

#include "EfiVfr.h"

PARSER_LINE_DEFINITION  *gLineDefinition = NULL;
PARSER_LINE_DEFINITION  *gLastLineDefinition = NULL;

VOID
AddFileLine (
  char      *TokenString,
  UINT32    TokenLine
  )
/*++

Routine Description:
  During the lexer phase, if we encounter a #line statement output by
  the preprocessor, this function gets called. We'll save off the info 
  for error reporting purposes. The preprocessor line information has the
  form:
    
    #line 3 "FileName.c"  

Arguments:
  TokenString - the parsed string as shown above
  TokenLine   - the line number in the preprocessed output file 
  
Returns:
  NA

--*/
{
  PARSER_LINE_DEFINITION  *LineDef;
  INT8                    *Cptr;
  
  //
  // Allocate a structure in which we can keep track of this line information.
  //
  LineDef = (PARSER_LINE_DEFINITION *)malloc (sizeof (PARSER_LINE_DEFINITION));
  memset ((char *)LineDef, 0, sizeof (PARSER_LINE_DEFINITION));
  LineDef->TokenLineNum = TokenLine;
  LineDef->HashLineNum = atoi (TokenString + 6);
  //
  // Find the quotes in the filename, then allocate space in the line
  // def structure for a copy of the filename. Finally, copy it without
  // quotes to the line def.
  //
  for (Cptr = TokenString + 7; *Cptr && (*Cptr != '"'); Cptr++);
  if (*Cptr == '"') {
    LineDef->FileName = (INT8 *)malloc (strlen (Cptr));
    Cptr++;
    strcpy (LineDef->FileName, Cptr);
    for (Cptr = LineDef->FileName; *Cptr && (*Cptr != '"'); Cptr++);
    *Cptr = 0;   
    //
    // Now add this new one to the list
    //
    if (gLineDefinition == NULL) {
      gLineDefinition = LineDef;
    } else {
      gLastLineDefinition->Next = LineDef;
    }
    gLastLineDefinition = LineDef;
  } else {
    Error (PROGRAM_NAME, 0, 0, "invalid line definition in preprocessor output file", TokenString);
    free (LineDef);
    return;
  }
}
char *
ConvertLineNumber (
  UINT32 *LineNum
  )
/*++

Routine Description:
  Given the line number in the preprocessor-output file, use the line number
  information we've saved to determine the source file name and line number
  where the code originally came from. This is required for error reporting.

Arguments:
  LineNum - the line number in the preprocessor-output file.

Returns:
  Returns a pointer to the source file name. Also returns the line number 
  in the provided LineNum argument

--*/
{
  PARSER_LINE_DEFINITION  *LineDef;
  //
  // Step through our linked list of #line information we saved off. 
  // For each one, look at its line number, and the line number of the
  // next record, and see if the passed-in line number is in the range.
  // If it is, then convert the line number to the appropriate line number
  // of the original source file.
  //
  for (LineDef = gLineDefinition; LineDef != NULL; LineDef = LineDef->Next) {
    //
    // The given LineNum is the line number from the .i file.
    // Find a line definition whose range includes this line number,
    // convert the line number, and return the filename.
    //
    if (LineDef->TokenLineNum <= *LineNum) {
      if (LineDef->Next != NULL) {
        if (LineDef->Next->TokenLineNum > *LineNum) {
          *LineNum = *LineNum - LineDef->TokenLineNum + LineDef->HashLineNum;
          return LineDef->FileName;
        }
      } else {
        //
        // Last one in the list of line definitions, so has to be right
        //
        *LineNum = *LineNum - LineDef->TokenLineNum + LineDef->HashLineNum;
        return LineDef->FileName;
      }
    }
  }
  return NULL;
}

>>

//
// Define a lexical class for parsing quoted strings. Basically
// starts with a double quote, and ends with a double quote that
// is not preceeded with a backslash.
//
#lexclass QUOTED_STRING
#token TheString            "~[\"]*\"" << mode (START); >>     

//
// Define a lexical class for parsing "#pragma pack" statements. 
// We do this just for convenience (since we skip them here) so
// that users can include some minimal .h files.
//
#lexclass PRAGMA_PACK
#token "pack"     << skip (); >>
#token "[\ \t]"   << skip (); >> 
#token "\("       << skip (); >>
#token "[0-9]*"   << skip (); >>
#token "\)"       << skip (); mode (START); >>

//
// Define a lexclass for skipping over C++ style comments
//
#lexclass CPP_COMMENT
#token "~[\n]*"       << skip (); >>
#token "\n"           << skip (); mode (START); newline (); >>

//
// Standard lexclass is START
//
#lexclass START

//
// Find start of C++ style comments
//
#token "//"     << skip (); mode (CPP_COMMENT); >>

//
// Skip whitespace
//
#token "[\ \t]"   << skip (); >> 

//
// Skip over newlines, but count them
//
#token "\n"       << skip (); newline (); >>

//
// Skip pragma pack statements
//
#token "\#pragma" << skip (); mode(PRAGMA_PACK); >>

//
// Skip over 'extern' in any included .H file
//
#token "extern"   << skip (); >>

//
// Tokens for the different keywords. Syntax is:
// TokenName("ErrorMessageText")    "TokenString"
//   where:
//     TokenName is the token name (must be capitalized) that is used in the rules
//     ErrorMessageText is the string the compiler emits when it detects a syntax error
//     TokenString is the actual matching string used in the user script
//
#token LineDefinition                           "#line\ [0-9]+\ \"~[\"]+\"[\ \t]*\n" << AddFileLine (begexpr (), line ()); skip (); >>
#token FormSet("formset")                       "formset"
#token EndFormSet("endformset")                 "endformset"
#token Title("title")                           "title"
#token FormId("formid")                         "formid"
#token OneOf("oneof")                           "oneof"
#token Prompt("prompt")                         "prompt"
#token OrderedList("orderedlist")               "orderedlist"
#token EndList("endlist")                       "endlist"
#token EndForm("endform")                       "endform"
#token EndOneOf("endoneof")                     "endoneof"
#token Form("form")                             "form"
#token Subtitle("subtitle")                     "subtitle"
#token Help("help")                             "help"
#token VarId("varid")                           "varid"
#token Text("text")                             "text"
#token Option("option")                         "option"
#token Value("value")                           "value"
#token Flags("flags")                           "flags"
#token Date("date")                             "date"
#token EndDate("enddate")                       "enddate"
#token Year("year")                             "year"
#token Month("month")                           "month"
#token Day("day")                               "day"
#token Time("time")                             "time"
#token EndTime("endtime")                       "endtime"
#token Hour("hour")                             "hour"
#token Minute("minute")                         "minute"
#token Second("second")                         "second"
#token AND("AND")                               "AND"
#token OR("OR")                                 "OR"
#token GrayOutIf("grayoutif")                   "grayoutif"
#token NOT("NOT")                               "NOT"
#token Label("label")                           "label"
#token Timeout("timeout")                       "timeout"
#token Inventory("inventory")                   "inventory"
#token StringToken("STRING_TOKEN")              "STRING_TOKEN"
#token NonNvDataMap("_NON_NV_DATA_MAP")         "_NON_NV_DATA_MAP"
#token Struct("struct")                         "struct"
#token Uint64("UINT64")                         "UINT64"
#token Uint32("UINT32")                         "UINT32"
#token Uint16("UINT16")                         "UINT16"
#token Char16("CHAR16")                         "CHAR16"
#token Uint8("UINT8")                           "UINT8"
#token Guid("guid")                             "guid"
#token CheckBox("checkbox")                     "checkbox"
#token EndCheckBox("endcheckbox")               "endcheckbox"
#token Numeric("numeric")                       "numeric"
#token EndNumeric("endnumeric")                 "endnumeric"            
#token Minimum("minimum")                       "minimum"         
#token Maximum("maximum")                       "maximum"         
#token Step("step")                             "step"      
#token Default("default")                       "default"         
#token Password("password")                     "password"          
#token EndPassword("endpassword")               "endpassword"             
#token String("string")                         "string"        
#token EndString("endstring")                   "endstring"           
#token MinSize("minsize")                       "minsize"         
#token MaxSize("maxsize")                       "maxsize"         
#token Encoding("encoding")                     "encoding"
#token SuppressIf("suppressif")                 "suppressif"
#token Hidden("hidden")                         "hidden"
#token Goto("goto")                             "goto"
#token InconsistentIf                           "inconsistentif"
#token EndIf("endif")                           "endif"
#token IdEqId("ideqid")                         "ideqid"
#token IdEqVal("ideqval")                       "ideqval"
#token VarEqVal("vareqval")                     "vareqval"
#token Var("var")                               "var"
#token IdEqValList("ideqvallist")               "ideqvallist"
#token Length("length")                         "length"
#token Values("values")                         "values"
#token Key("key")                               "key"
#token DefaultFlag("DEFAULT")                   "DEFAULT"
#token ManufacturingFlag("MANUFACTURING")       "MANUFACTURING"
#token InteractiveFlag("INTERACTIVE")           "INTERACTIVE"
#token NVAccessFlag("NV_ACCESS")                "NV_ACCESS"
#token ResetRequiredFlag("RESET_REQUIRED")      "RESET_REQUIRED"
#token LateCheckFlag("LATE_CHECK")              "LATE_CHECK"
#token Class("class")                           "class"
#token Subclass("subclass")                     "subclass"
#token TypeDef("typedef")                       "typedef"
#token Restore("restore")                       "restore"
#token Save("save")                             "save"
#token Defaults("defaults")                     "defaults"
#token Banner("banner")                         "banner"
#token Align("align")                           "align"
#token Left("left")                             "left"
#token Right("right")                           "right"
#token Center("center")                         "center"
#token Line("line")                             "line"
#token VarStore("varstore")                     "varstore"
#token Name("name")                             "name"
#token Oem("oem")                               "oem"
#token True("TRUE")                             "TRUE"
#token False("FALSE")                           "FALSE"
#token GreaterThan(">")                         ">"
#token GreaterEqual(">=")                       ">="
#token LessThan("<")                          "<"
#token LessEqual("<=")                        "<="

//
// Define the class and subclass tokens
//
#token ClassNonDevice("NONDEVICE")                        "NON_DEVICE"
#token ClassDiskDevice("DISK_DEVICE")                     "DISK_DEVICE"
#token ClassVideoDevice("VIDEO_DEVICE")                   "VIDEO_DEVICE"
#token ClassNetworkDevice("NETWORK_DEVICE")               "NETWORK_DEVICE"
#token ClassInputDevice("INPUT_DEVICE")                   "INPUT_DEVICE"
#token ClassOnBoardDevice("ONBOARD_DEVICE")               "ONBOARD_DEVICE"
#token ClassOtherDevice("OTHER_DEVICE")                   "OTHER_DEVICE"

#token SubclassSetupApplication("SETUP_APPLICATION")      "SETUP_APPLICATION"
#token SubclassGeneralApplication("GENERAL_APPLICATION")  "GENERAL_APPLICATION"
#token SubclassFrontPage("FRONT_PAGE")                    "FRONT_PAGE"
#token SubclassSingleUse("SINGLE_USE")                    "SINGLE_USE"

#token LanguageIdentifier("language identifier") "[a-z][a-z][a-z]"   // 3 lowercase characters
#token StringIdentifier("string identifier")    "[A-Za-z_][A-Za-z_0-9]*"
#token Number("numeric value")                  "(0x[0-9A-Fa-f]+) | [0-9]+"
#token OpenBrace("{")                           "\{"
#token CloseBrace("}")                          "\}"
#token OpenParen("(")                           "\("
#token CloseParen(")")                          "\)"
#token OpenBracket("[")                         "\["
#token CloseBracket("]")                        "\]"

//
// Define all other invalid characters so that they get through the lexical phase
// and we can catch them during the parse phase. We get much better error
// messages then. 
//
#token InvalidCharacters("invalid characters")  "~[;:=,\.\|]"  

//
// This is the overall definition of a VFR form definition script.
//
program :
  ( dataStructDefinition )*
  formSetStatement   
  ( vfrStatementVarStore )*
  ( formDefinition )*
  EFS:EndFormSet  ";"                   << WriteOpByte (EFS->getLine(), EFI_IFR_END_FORM_SET_OP); >>
  "@" // end of file
  ;
    
formSetStatement :
  FS:FormSet                            << WriteOpByte (FS->getLine(), EFI_IFR_FORM_SET_OP); >>
  Guid "=" 
  OpenBrace 
  G1:Number ","
  G2:Number ","
  G3:Number ","
  G4:Number ","
  G5:Number ","
  G6:Number ","
  G7:Number ","
  G8:Number ","
  G9:Number ","
  G10:Number ","
  G11:Number 
  CloseBrace                            << WriteGuidValue (G1->getLine (), G1->getText (), G2->getText (), G3->getText (),
                                                           G4->getText (), G5->getText (), G6->getText (), G7->getText (),
                                                           G8->getText (), G9->getText (), G10->getText (), G11->getText ()
                                                          );
                                         >>
  ","
  Title "=" getStringId ","
  Help  "=" getStringId ","
  //
  // insert padding for an EFI_PHYSICAL_ADDRESS (UINT64)
  //
                                            << WriteDWord (0, 0); WriteDWord (0, 0); >>
  Class "=" CVAL:classDefinition ","        << WriteClass (); >>
  Subclass "=" SVAL:subclassDefinition ","  << WriteSubclass (); >>
                                            << WriteWord (mNvDataStructSize); >>
  ;  

//
// A form can be of multiple classes, thus allow CLASS_A | CLASS_B | CLASS_C
//
classDefinition :
  validClassNames ( "\|" validClassNames )*
  ;
  
validClassNames :
    CND:ClassNonDevice          << SetClass (CND->getLine(), EFI_NON_DEVICE_CLASS); >>
  | CDD:ClassDiskDevice         << SetClass (CDD->getLine(), EFI_DISK_DEVICE_CLASS); >>
  | CVD:ClassVideoDevice        << SetClass (CVD->getLine(), EFI_VIDEO_DEVICE_CLASS); >>
  | CNW:ClassNetworkDevice      << SetClass (CNW->getLine(), EFI_NETWORK_DEVICE_CLASS); >>
  | CID:ClassInputDevice        << SetClass (CID->getLine(), EFI_INPUT_DEVICE_CLASS); >>
  | COB:ClassOnBoardDevice      << SetClass (COB->getLine(), EFI_ON_BOARD_DEVICE_CLASS); >>
  | COD:ClassOtherDevice        << SetClass (COD->getLine(), EFI_OTHER_DEVICE_CLASS); >>
  | CNUM:Number                 << SetClass (CNUM->getLine(), GetNumber (CNUM->getText(), CNUM->getLine(), 4)); >>
  ; << PrintErrorMessage (LT(1)->getLine(), LT(1)->getText(), "invalid class"); >>

//
// A form can only be of one subclass type.
//
subclassDefinition :
    SSA:SubclassSetupApplication    << SetSubclass (SSA->getLine(), EFI_SETUP_APPLICATION_SUBCLASS); >>
  | SGA:SubclassGeneralApplication  << SetSubclass (SGA->getLine(), EFI_GENERAL_APPLICATION_SUBCLASS); >>
  | SFP:SubclassFrontPage           << SetSubclass (SFP->getLine(), EFI_FRONT_PAGE_SUBCLASS); >>
  | SSU:SubclassSingleUse           << SetSubclass (SSU->getLine(), EFI_SINGLE_USE_SUBCLASS); >>
  | SNUM:Number                     << SetSubclass (SNUM->getLine(), GetNumber (SNUM->getText(), SNUM->getLine(), 4)); >>
  ; << PrintErrorMessage (LT(1)->getLine(), LT(1)->getText(), "invalid subclass"); >>

//
// Parse a C type data structure for storing VFR setup data. Allow:
//  typedef struct _XXX_ {
//     (fields)
//  } MY_NV_DATA;
//
dataStructDefinition :
  << int IsNonNV = 0; >>
  { TypeDef } 
  S:Struct                          
  (
    NonNvDataMap                    << IsNonNV = 1; >>
  |
    { StringIdentifier }
  )                                 << StartStructDefinition (IsNonNV, S->getLine()); >>
  OpenBrace 
  dataStructFields 
  CloseBrace NAME:StringIdentifier  << EndStructDefinition (NAME->getText(), NAME->getLine()); >>
  ";"
  ;

//
// Parse a C type data structure for defining data that is not stored in NV.
//  typedef struct _NON_NV_DATA_MAP {
//     (fields)
//  } NON_NV_DATA_MAP;
//
nonNvDataStructDefinition :
  { TypeDef } 
  Struct NonNvDataMap
  { StringIdentifier }
  OpenBrace 
  dataStructFields 
  CloseBrace NAME:StringIdentifier        << AddStructField (NAME->getText(), NAME->getLine(), 0, 0, 0); >>
  ";"                                             
  ;

dataStructFields :
  ( dataStructField64 | dataStructField32 | dataStructField16 | dataStructField8 ) *
  ;

//*****************************************************************************
//
// PARSE:
//   UINT64 Name[4];
//   UINT64 Name;
//
// Used while parsing the NV data map structures.
//
dataStructField64 :
  << int ArrayLength = 1; char IsArray = 0; >>
  "UINT64" 
  NAME:StringIdentifier 
  ( ";" | OpenBracket IVal:Number CloseBracket ";"  << ArrayLength = GetNumber (IVal->getText(), IVal->getLine(), 4); IsArray = 1; >> ) 
                                                    << AddStructField (NAME->getText(), NAME->getLine(), 8, ArrayLength, IsArray); >>
  ;

//*****************************************************************************
//
// PARSE:
//   UINT32 Name[4];
//   UINT32 Name;
//
// Used while parsing the NV data map structures.
//
dataStructField32 :
  << int ArrayLength = 1; char IsArray = 0; >>
  "UINT32" 
  NAME:StringIdentifier 
  ( ";" | OpenBracket IVal:Number CloseBracket ";"  << ArrayLength = GetNumber (IVal->getText(), IVal->getLine(), 4); IsArray = 1; >> )  
                                                    << AddStructField (NAME->getText(), NAME->getLine(), 4, ArrayLength, IsArray); >>
  ;

//*****************************************************************************
//
// PARSE:
//   UINT16 Name[4];
//   UINT16 Name;
//
// Used while parsing the NV data map structures.
//
dataStructField16 :
  << int ArrayLength = 1; char IsArray = 0; >>
  ( "UINT16" | "CHAR16" )
  NAME:StringIdentifier 
  ( ";" | OpenBracket IVal:Number CloseBracket ";"  << ArrayLength = GetNumber (IVal->getText(), IVal->getLine(), 4); IsArray = 1; >> ) 
                                                    << AddStructField (NAME->getText(), NAME->getLine(), 2, ArrayLength, IsArray); >>
  ;

//*****************************************************************************
//
// PARSE:
//   UINT8 Name[4];
//   UINT8 Name;
//
// Used while parsing the NV data map structures.
//
dataStructField8 :
  << int ArrayLength = 1; char IsArray = 0; >>
  "UINT8" 
  NAME:StringIdentifier 
  ( ";" | OpenBracket IVal:Number CloseBracket ";"  << ArrayLength = GetNumber (IVal->getText(), IVal->getLine(), 4); IsArray = 1; >> ) 
                                                    << AddStructField (NAME->getText(), NAME->getLine(), 1, ArrayLength, IsArray); >>
  ;

//*****************************************************************************
//
// PARSE:
//    form formid = 1,
//      title  = STRING_TOKEN(STR_FORM_TITLE);
//      -- form statements --
//    endform;
//
//  The Form ID cannot be 0
//
formDefinition :
  FRM:Form FormId                << WriteOpByte (FRM->getLine(), EFI_IFR_FORM_OP); >> 
  "=" 
  VAL:Number                     << WriteWord (GetNumber (VAL->getText(), VAL->getLine(), 2)); AddFormId (GetNumber (VAL->getText(), VAL->getLine(), 2), VAL->getLine()); >>
  ","
  Title "=" getStringId ";"      // writes string identifier
  ( vfrStatements )*
  ENDF:EndForm  ";"              << WriteOpByte (ENDF->getLine(), EFI_IFR_END_FORM_OP); >>
  ;

//
// VFR statements in a formset
//
vfrStatements :
  vfrStatementSubTitle        | 
  vfrStatementOneOf           |
  vfrStatementTextText        |
  vfrStatementCheckBox        |
  vfrStatementNumeric         |
  vfrStatementDate            |
  vfrStatementTime            |
  vfrStatementPassword        |
  vfrStatementString          |
  vfrStatementSuppressIf      |
  vfrStatementHidden          |
  vfrStatementGoto            | 
  vfrStatementGrayOutIf       |
  vfrStatementInconsistentIf  |
  vfrStatementLabel           |
  vfrStatementBanner          |
  vfrStatementInventory       |
  vfrStatementOrderedList     |
  vfrStatementOem             |
  vfrStatementSaveRestoreDefaults
  ;

//*****************************************************************************
//
// PARSE:
//   label 100;
//
vfrStatementLabel :
  OPID:Label                              << WriteOpByte (OPID->getLine(), EFI_IFR_LABEL_OP); >>
  VAL:Number                              << 
                                              WriteWord (GetNumber (VAL->getText(), VAL->getLine(), 2)); 
                                              AddLabel (GetNumber (VAL->getText(), VAL->getLine(), 2), VAL->getLine());
                                          >>
  ";"
  ;

//*****************************************************************************
//
// PARSE:
//   oem 0x12, 0x34, 0x56;
//
vfrStatementOem :
  OPID:Oem                              << WriteOpByte (OPID->getLine(), EFI_IFR_OEM_DEFINED_OP); >>
  ( VAL1:Number << WriteByte (GetNumber (VAL1->getText(), VAL1->getLine(), 1), 0); >> )
  ( "," VAL2:Number << WriteByte (GetNumber (VAL2->getText(), VAL2->getLine(), 1), 0); >> )*
  ";"
  ;
  
//*****************************************************************************
//
// PARSE:
//   inconsistentif NOT .... AND NOT .... OR ... endif;
//
vfrStatementInconsistentIf : 
  << ResetFlags (); >>
  IIFOP:InconsistentIf                  << WriteOpByte (IIFOP->getLine(), EFI_IFR_INCONSISTENT_IF_OP); >>
  Prompt "=" getStringId ","
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* "," 
  }
  << WriteFlags (); >> //  write the flags field
  vfrBooleanExpression
  EOP:EndIf ";"                         << WriteOpByte (EOP->getLine(), EFI_IFR_END_IF_OP); >>
  ;

//*****************************************************************************
// 
// PARSE:
//   TRUE AND (ideqval SomeStruct.SomeMember >= 0x10 OR 
//               ideqid SomeStruct.SomeMember < SomeStruct.SomeOtherMember) AND
//            (ideqlist SomeStruct.SomeOtherMember == 0x10, 0x20, 0x30 OR
//               vareqval var(VAR_EQ_TEST_NAME) == 0x1)
//
// For supporting complex express, divide the vfrBooleanExpression to two parts
// so that pred-LL(k) parser can parse incrementally.
//
vfrBooleanExpression :
  leftPartVfrBooleanExp { rightPartVfrBooleanExp }
  ;
  
leftPartVfrBooleanExp :
  OpenParen vfrBooleanExpression CloseParen                                                        |
  (ideqval | ideqid | ideqvallist | vareqval | truefalse)                                          |
  NOPID:NOT leftPartVfrBooleanExp           << WriteOpByte (NOPID->getLine(), EFI_IFR_NOT_OP); >>
  ;

rightPartVfrBooleanExp :
  AOPID:AND vfrBooleanExpression            << WriteOpByte (AOPID->getLine(), EFI_IFR_AND_OP); >>  |
  OOPID:OR vfrBooleanExpression             << WriteOpByte (OOPID->getLine(), EFI_IFR_OR_OP); >>
  ;

//*****************************************************************************
//
// PARSE:
//   TRUE
//
truefalse :
  TOPID:True                                << WriteOpByte (TOPID->getLine(), EFI_IFR_TRUE_OP); >> |
  FOPID:False                               << WriteOpByte (FOPID->getLine(), EFI_IFR_FALSE_OP); >>
  ;

//*****************************************************************************
//
// PARSE:
//   varstore MY_STRUCT_NAME, key = 0x1234, name = "MyVariableName", guid = {...};
//
vfrStatementVarStore : 
  OP:VarStore                           << WriteOpByte (OP->getLine(), EFI_IFR_VARSTORE_OP); >>
  STRUCT_NAME:StringIdentifier ","
  Key   "=" KNUM:Number ","
  Name  "=" VAR_NAME:StringIdentifier ","  
  Guid "=" 
  OpenBrace 
  G1:Number ","
  G2:Number ","
  G3:Number ","
  G4:Number ","
  G5:Number ","
  G6:Number ","
  G7:Number ","
  G8:Number ","
  G9:Number ","
  G10:Number ","
  G11:Number 
  CloseBrace                            << WriteGuidValue (G1->getLine (), G1->getText (), G2->getText (), G3->getText (),
                                                           G4->getText (), G5->getText (), G6->getText (), G7->getText (),
                                                           G8->getText (), G9->getText (), G10->getText (), G11->getText ()
                                                          );
                                           WriteWord (GetNumber (KNUM->getText(), KNUM->getLine(), 2)); 
                                           AddVarStore (STRUCT_NAME->getText(), VAR_NAME->getText(), GetNumber (KNUM->getText(), KNUM->getLine(), 2), STRUCT_NAME->getLine());
                                         >>
  
  ";"
  ;

//*****************************************************************************
//
// PARSE:  
//   vareqval var(0x100) == 0x20
//
vareqval : 
  OPID:VarEqVal                           << WriteOpByte (OPID->getLine(), EFI_IFR_EQ_VAR_VAL_OP); >>
  Var OpenParen 
  VAR:Number                              << WriteWord (GetNumber (VAR->getText(), VAR->getLine(), 2)); >>
  CloseParen
  compareNumber
  ;

ideqval : 
  OPID:IdEqVal                            << WriteOpByte (OPID->getLine(), EFI_IFR_EQ_ID_VAL_OP); >>
  vfrStructFieldName[0]
  compareNumber
  ;

//*****************************************************************************
//
// PARSE:
//   ideqid MyNVData3.Field16A == MyNVData3.Field16B
//
// NOTE: Before processing the second variable store in the ideqid statement, set a global flag
//       so that when we parse the second variable we set the secondary variable store id.
//
ideqid : 
  OPID:IdEqId                             << WriteOpByte (OPID->getLine(), EFI_IFR_EQ_ID_ID_OP);  >>
  vfrStructFieldName[0]
  compareVfrStructFieldNameNL0
  ;

//*****************************************************************************
//
// compareNumber is the combination of compare operation and Number
//
compareNumber :
  (
  "=="
  VAL1:Number                             << WriteWord (GetNumber (VAL1->getText(), VAL1->getLine(), 2)); >>
  ) |
  (
  GTOPID:GreaterThan
  VAL2:Number                             << WriteWord (GetNumber (VAL2->getText(), VAL2->getLine(), 2));
                                             WriteOpByte (GTOPID->getLine(), EFI_IFR_GT_OP); >>
  ) |
  (
  GEOPID:GreaterEqual
  VAL3:Number                             << WriteWord (GetNumber (VAL3->getText(), VAL3->getLine(), 2));
                                             WriteOpByte (GEOPID->getLine(), EFI_IFR_GE_OP); >>
  ) |
  (
  LTOPID:LessThan
  VAL4:Number                             << WriteWord (GetNumber (VAL4->getText(), VAL4->getLine(), 2));
                                             WriteOpByte (LTOPID->getLine(), EFI_IFR_GE_OP);
                                             WriteOpByte (LTOPID->getLine(), EFI_IFR_NOT_OP); >>
  ) |
  (
  LEOPID:LessEqual
  VAL5:Number                             << WriteWord (GetNumber (VAL5->getText(), VAL5->getLine(), 2));
                                             WriteOpByte (LEOPID->getLine(), EFI_IFR_GT_OP);
                                             WriteOpByte (LEOPID->getLine(), EFI_IFR_NOT_OP); >>
  )
  ;

//*****************************************************************************
//
// compareVfrStructFieldNameNL0 is the combination of compare operation and  vfrStructFieldNameNL[0]
//
compareVfrStructFieldNameNL0 :
  (
  "=="                                    << mIdEqIdStmt = 1; >>
  vfrStructFieldNameNL[0]                 << mIdEqIdStmt = 0; >>
  ) |
  (
  GTOPID:GreaterThan                      << mIdEqIdStmt = 1; >>
  vfrStructFieldNameNL[0]                 << mIdEqIdStmt = 0;
                                             WriteOpByte (GTOPID->getLine(), EFI_IFR_GT_OP); >>
  ) |
  (
  GEOPID:GreaterEqual                     << mIdEqIdStmt = 1; >>
  vfrStructFieldNameNL[0]                 << mIdEqIdStmt = 0;
                                             WriteOpByte (GEOPID->getLine(), EFI_IFR_GE_OP); >>
  ) |
  (
  LTOPID:LessThan                       << mIdEqIdStmt = 1; >>
  vfrStructFieldNameNL[0]                 << mIdEqIdStmt = 0;
                                             WriteOpByte (LTOPID->getLine(), EFI_IFR_GE_OP);
                                             WriteOpByte (LTOPID->getLine(), EFI_IFR_NOT_OP); >>
  ) |
  (
  LEOPID:LessEqual                      << mIdEqIdStmt = 1; >>
  vfrStructFieldNameNL[0]                 << mIdEqIdStmt = 0;
                                             WriteOpByte (LEOPID->getLine(), EFI_IFR_GT_OP);
                                             WriteOpByte (LEOPID->getLine(), EFI_IFR_NOT_OP); >>
  )
  ;
  

ideqvallist : 
  OPID:IdEqValList                        << WriteOpByte (OPID->getLine(), EFI_IFR_EQ_ID_LIST_OP); >>
  vfrStructFieldName[0] 
  "=="
  ( VAL:Number                            << QueueIdEqValList (GetNumber (VAL->getText(), VAL->getLine(), 2)); >> ) +
                                          << FlushQueueIdEqValList(); >>
  ;
    
vfrStatementGoto : 
  << UINT32 LineNum, KeyValue = 0; ResetFlags (); >>
  IDG:Goto                          << WriteOpByte (IDG->getLine(), EFI_IFR_REF_OP); >>
  VAL:Number  ","                   << WriteWord (GetNumber (VAL->getText(), VAL->getLine(), 2)); 
                                       AddGotoReference (GetNumber (VAL->getText(), VAL->getLine(), 2), VAL->getLine());
                                    >>
  KP:Prompt   "=" getStringId ","   << LineNum = KP->getLine();  >>
  Help        "=" getStringId
  { 
    "," 
    FF:Flags  "=" flagsField ( "\|" flagsField )*  << LineNum = FF->getLine(); >>
  }
  {
    "," Key   "=" KNUM:Number       << LineNum = KNUM->getLine(); KeyValue = GetNumber(KNUM->getText(), LineNum, 2); >>
  }
                                    << WriteFlagsKey (KeyValue, LineNum); >>
  ";"
  ;
    
vfrStatementHidden : 
  IDH:Hidden                  << WriteOpByte (IDH->getLine(), EFI_IFR_HIDDEN_OP); >>
  Value "="
  VAL:Number ","              << WriteWord (GetNumber (VAL->getText(), VAL->getLine(), 2)); >>
  Key "="
  KVAL:Number                 << WriteWord (GetNumber (KVAL->getText(), KVAL->getLine(), 2)); >>
  ";"
  ;    

//*****************************************************************************
//
// PARSE:
//   suppressif <boolean_expression> { grayoutif } <statements>+ endif;
// Note:
//   You can have: suppressif:grayoutif:statements:endif
//                 suppressif:grayoutif:endif                  -- serves no purpose
//                 suppressif:statements:endif
//                 suppressif:endif                            -- serves no purpose
//
vfrStatementSuppressIf : 
  << ResetFlags (); >>
  OPID:SuppressIf                     << WriteOpByte (OPID->getLine(), EFI_IFR_SUPPRESS_IF_OP); SetIfStart (OPID->getLine()); >>
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* ","
  }
  << WriteFlags (); >> //  write the flags field 
  vfrBooleanExpression
  ";"
  { suppressIfGrayOutIf } ( suppressIfAndGrayoutIfSubstatements )+
  ENDOP:EndIf ";"                     << WriteOpByte (ENDOP->getLine(), EFI_IFR_END_IF_OP); SetIfStart (0); >>
  ;

//
// This is the form for a grayoutif nested in a suppressif statement
//
suppressIfGrayOutIf :
  << ResetFlags (); >>
  OPID:GrayOutIf                      << WriteOpByte (OPID->getLine(), EFI_IFR_GRAYOUT_IF_OP); >>
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* "," 
  }
  << WriteFlags (); >> //  write the flags field
  vfrBooleanExpression
  ";"
  ; 

//*****************************************************************************
//
// PARSE:
//   grayoutif { flags = n, } <boolean_expression> endif;
// Note:
//   You can have: grayoutif:suppressif:statements:endif
//                 grayoutif:statements:endif
//
//
vfrStatementGrayOutIf :
  << ResetFlags (); >>
  OPID:GrayOutIf                      << WriteOpByte (OPID->getLine(), EFI_IFR_GRAYOUT_IF_OP); SetIfStart (OPID->getLine()); >>
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* "," 
  }
  << WriteFlags (); >> //  write the flags field
  vfrBooleanExpression
  ";"
  { grayoutIfSuppressIf } ( suppressIfAndGrayoutIfSubstatements )+ 
  ENDOP:EndIf ";"                     << WriteOpByte (ENDOP->getLine(), EFI_IFR_END_IF_OP); SetIfStart (0); >>
  ;

//
// This is the format for a suppressif nested in a grayoutif
//
grayoutIfSuppressIf : 
  << ResetFlags (); >>
  OPID:SuppressIf                     << WriteOpByte (OPID->getLine(), EFI_IFR_SUPPRESS_IF_OP); >>
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* ","
  }
  << WriteFlags (); >> //  write the flags field
  vfrBooleanExpression
  ";"
  ;

//
// These are the VFR statements that are valid inside a suppressif or grayoutif statement.
//
suppressIfAndGrayoutIfSubstatements :
  vfrStatementOneOf           |
  vfrStatementTextText        |
  vfrStatementCheckBox        |
  vfrStatementNumeric         |
  vfrStatementDate            |
  vfrStatementTime            |
  vfrStatementPassword        |
  vfrStatementString          |
  vfrStatementHidden          |
  vfrStatementGoto            | 
  vfrStatementLabel           |
  vfrStatementInventory       |
  vfrStatementOrderedList     |
  vfrStatementSaveRestoreDefaults
  ; 

//*****************************************************************************
//
// PARSE:
//
//    password  varid    = MyNvData.Password,
//              prompt   = STRING_TOKEN(STR_PASSWORD_PROMPT),
//              help     = STRING_TOKEN(STR_PASSWORD_HELP),
//              minsize  = 6,
//              maxsize  = 20,
//              encoding = 1,
//    endpassword; 
  
vfrStatementPassword : 
  << UINT32 KeyValue = 0; UINT32 LineNum; ResetFlags (); >>
  IDPW:Password                       << WriteOpByte (IDPW->getLine(), EFI_IFR_PASSWORD_OP); >>
  VarId       "=" vfrStructFieldNameArray[0] ","
  Prompt      "=" getStringId ","
  KH:Help     "=" getStringId ","    << LineNum = KH->getLine(); >>
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* ","  << LineNum = FF->getLine(); >>
  }
  {
    Key "=" KNUM:Number ","           << LineNum = KNUM->getLine(); KeyValue = GetNumber(KNUM->getText(), LineNum, 2); >>
  }
                                      << WriteFlagsKey (KeyValue, LineNum); >>
  MinSize   "=" MIN:Number ","        << WriteByte (GetNumber (MIN->getText(), MIN->getLine(), 1), 0); >>
  MaxSize   "=" MAX:Number ","        << WriteByte (GetNumber (MAX->getText(), MAX->getLine(), 1), 0); >>
  Encoding  "=" ENC:Number ","        << WriteWord (GetNumber (ENC->getText(), ENC->getLine(), 2)); >>
  EndPassword  ";"              
  ;

//*****************************************************************************
//
//  PARSE:
//
//    string    varid    = MyNv.String,
//              prompt   = STRING_TOKEN(STR_STRING_PROMPT),
//              help     = STRING_TOKEN(STR_STRING_HELP),
//              flags    = INTERACTIVE,
//              key      = 0x1234,
//              minsize  = 6,
//              maxsize  = 0x14,
//    endstring; 
//
// Since flags and key are optional, we can't use Flags->getLine(). Therefore for error
// reporting we save the line number of the "help" keyword.
//
vfrStatementString : 
  << unsigned int KeyValue = 0; UINT32 LineNum; ResetFlags (); >>
  IDS:String                                << WriteOpByte (IDS->getLine(), EFI_IFR_STRING_OP); >>
  VarId     "=" vfrStructFieldNameArray[0] ","
  Prompt    "=" getStringId ","
  KH:Help   "=" getStringId ","             << LineNum = KH->getLine(); >>
  { 
    FF:Flags "=" 
    flagsField ( "\|" flagsField )*         << LineNum = FF->getLine(); >>
    "," 
  }
  {
    Key "=" KNUM:Number ","                 << LineNum = KNUM->getLine(); KeyValue = GetNumber(KNUM->getText(), LineNum, 2); >>
  }
                                            << WriteFlagsKey (KeyValue, LineNum); >>
  MinSize   "=" MIN:Number ","              << WriteByte (GetNumber (MIN->getText(), MIN->getLine(), 1), 0);  >>
  MaxSize   "=" MAX:Number ","              << WriteByte (GetNumber (MAX->getText(), MAX->getLine(), 1), 0); >>
  EndString  ";"
  ;

//*****************************************************************************
//
// PARSE:
//    numeric varid   = MyIfrNVData.HowOldAreYouInYears, 
//            prompt  = STRING_TOKEN(STR_NUMERIC_PROMPT),
//            help    = STRING_TOKEN(STR_NUMERIC_HELP),
//            flags   = INTERACTIVE,  // flags is optional
//            key     = 0x1234,       // key is optional if (flags & INTERACTIVE = 0)
//            minimum = 0x0,
//            maximum = 0xf0,
//            step    = 1,            // step is option, and step=1 if not specified
//            default = 0;            // default is optional, and default=minimum if not specified
//    endnumeric;
//
// Make flags and key optional. However if flags includes INTERACTIVE, then a key is required.
// That check is done in WriteFlagsKey() function.
//
vfrStatementNumeric :  
  << UINT32 LineNum, KeyValue = 0; ResetFlags (); >>
  IDN:Numeric                         << WriteOpByte (IDN->getLine(), EFI_IFR_NUMERIC_OP); >>
  VarId     "=" vfrStructFieldName[2] ","
  Prompt    "=" getStringId ","
  KH:Help   "=" getStringId ","       << LineNum = KH->getLine(); >>
  { 
    FF:Flags "=" flagsField ( "\|" flagsField )* ","     << LineNum = FF->getLine (); >>
  }
  {
    Key "=" KNUM:Number  ","          << LineNum = KNUM->getLine(); KeyValue = GetNumber(KNUM->getText(), LineNum, 2); >>
  }
                                      << WriteFlagsKey (KeyValue, LineNum); >>
  minMaxStepDefault                   
  EndNumeric ";"                      << WriteMinMaxStepDefault (); >>
  ;

//
// Parse minimum/maximum/step/default statements. Special cases:
//   - if step not specified, then the value is 1
//   - if default not specified, then the value is the min value specified
//   - if max < min, print a warning and swap the values (changes default too)
//
minMaxStepDefault :
  << InitMinMaxStepDefault (); >>
  Minimum   "=" MIN:Number ","        << SetMinMaxStepDefault (GetNumber (MIN->getText(),  MIN->getLine(), 2), 0, MIN->getLine()); >>
  Maximum   "=" MAX:Number ","        << SetMinMaxStepDefault (GetNumber (MAX->getText(),  MAX->getLine(), 2), 1, MAX->getLine()); >>
  { Step    "=" STEP:Number ","       << SetMinMaxStepDefault (GetNumber (STEP->getText(), STEP->getLine(), 2), 2, STEP->getLine()); >> }
  { Default "=" DEF:Number ","        << SetMinMaxStepDefault (GetNumber (DEF->getText(),  DEF->getLine(), 2), 3, DEF->getLine()); >> }
  ;


//*****************************************************************************
//
// PARSE:
//
//    date    year varid  = Date.Year,                        // "Date.Year" is a special case we recognize
//            prompt      = STRING_TOKEN(STR_DATE_PROMPT),
//            help        = STRING_TOKEN(STR_DATE_YEAR_HELP),
//            minimum     = 1939,
//            maximum     = 2101,
//            step        = 1,
//            default     = 1964,
//
//            month varid = Date.Month,    
//            prompt      = STRING_TOKEN(STR_DATE_PROMPT),
//            help        = STRING_TOKEN(STR_DATE_MONTH_HELP),
//            minimum     = 1,
//            maximum     = 12,
//            step        = 1,
//            default     = 1,
//
//            day varid   = Date.Day,
//            prompt      = STRING_TOKEN(STR_DATE_PROMPT),
//            help        = STRING_TOKEN(STR_DATE_DAY_HELP),
//            minimum     = 1,
//            maximum     = 31,
//            step        = 0x1,
//            default     = 1,
//
//    enddate;
//  
vfrStatementDate :  
  Date                            
  IDY:Year VarId "="                  << WriteOpByte (IDY->getLine(), EFI_IFR_DATE_OP); >>
  vfrStructFieldName[2] "," 
  dateTimeSubStatement                    
  IDM:Month VarId "="                 << WriteOpByte (IDM->getLine(), EFI_IFR_DATE_OP); >>
  vfrStructFieldName[2] "," 
  dateTimeSubStatement                    
  IDD:Day VarId "="                   << WriteOpByte (IDD->getLine(), EFI_IFR_DATE_OP); >> 
  vfrStructFieldName[2] ","  
  dateTimeSubStatement    
  EndDate ";"
  ;
  
vfrStatementTime :  
  Time                            
  IDH:Hour VarId "="                  << WriteOpByte (IDH->getLine(), EFI_IFR_TIME_OP); >>
  vfrStructFieldName[2] ","  
  dateTimeSubStatement                    
  IDM:Minute VarId "="                << WriteOpByte (IDM->getLine(), EFI_IFR_TIME_OP); >>
  vfrStructFieldName[2] "," 
  dateTimeSubStatement                    
  IDS:Second VarId "="                << WriteOpByte (IDS->getLine(), EFI_IFR_TIME_OP); >>
  vfrStructFieldName[2] "," 
  dateTimeSubStatement
  EndTime ";"
  ;

//*****************************************************************************
//
// PARSE:
//
//   text  text = STRING_ID;
//   text  text = STRING_ID, text = STRING_ID;
//   text  text = STRING_ID, text = STRING_ID, flags = x, key = y;
//
vfrStatementTextText :
  << ResetFlags (); >>
  IDT:Text                            << WriteOpByte (IDT->getLine(), EFI_IFR_TEXT_OP); >>
  Help "=" getStringId ","
  Text "=" 
  getStringId                         // writes string identifier
  { "," Text "=" getStringId
    "," Flags "=" flagsField ( "\|" flagsField )*  << WriteFlags (); >>
    "," 
    Key "=" KNUM:Number               << WriteWord (GetNumber(KNUM->getText(), KNUM->getLine(), 2)); >>
  }
  ";" 
  ;

//*****************************************************************************
//
// PARSE:
//
//   inventory help = ID, text = ID;
//   inventory help = ID, text = id, text = ID;
//
vfrStatementInventory :
  IDI:Inventory                        << WriteOpByte (IDI->getLine(), EFI_IFR_INVENTORY_OP); >>
  Help        "=" getStringId ","
  Text        "=" getStringId                 // writes string identifier
  { "," Text  "=" getStringId
  }
  ";" 
  ;

//*****************************************************************************
//
// PARSE:
//
//    restore defaults,
//      formid  = 4,
//      prompt  = STRING_TOKEN(STR_RESTORE_DEFAULTS_PROMPT),
//      help    = STRING_TOKEN(STR_RESTORE_DEFAULTS_HELP),
//      flags   = 0,
//      key     = 0;
//
//    save defaults,
//      formid  = 4,
//      prompt  = STRING_TOKEN(STR_SAVE_DEFAULTS_PROMPT),
//      help    = STRING_TOKEN(STR_SAVE_DEFAULTS_HELP),
//      flags   = 0,
//      key     = 0;
//
vfrStatementSaveRestoreDefaults : 
  << unsigned int KeyValue = 0; UINT32 LineNum; ResetFlags (); >>
  ( IDS:Save                            << WriteOpByte (IDS->getLine(), EFI_IFR_SAVE_DEFAULTS_OP); >>
  | IDR:Restore                         << WriteOpByte (IDR->getLine(), EFI_IFR_RESTORE_DEFAULTS_OP); >>
  )
  Defaults ","
  FormId    "=" FRMID:Number  ","       << WriteWord (GetNumber (FRMID->getText(), FRMID->getLine(), 2)); 
                                           AddGotoReference (GetNumber (FRMID->getText(), FRMID->getLine(), 2), FRMID->getLine());
                                        >>
  Prompt    "=" getStringId ","
  KH:Help   "=" getStringId             << LineNum = KH->getLine(); >>
  { 
    "," FF:Flags "=" flagsField ( "\|" flagsField )*  << LineNum = FF->getLine(); >>
  }
  {
    "," Key "=" KNUM:Number             << LineNum = KNUM->getLine(); KeyValue = GetNumber(KNUM->getText(), LineNum, 2); >>
  }
                                        << WriteFlagsKey (KeyValue, LineNum); >>
  ";"
  ;

//*****************************************************************************
//
// PARSE:
//
//   flags = 0x10 | DEFAULT | MANUFACTURING | INTERACTIVE | NV_ACCESS | RESET_REQUIRED | LATE_CHECK
//
// 
flagsField :
  VAL:Number                          << SetFlags (GetNumber(VAL->getText(), VAL->getLine(), 4), VAL->getLine()); >>
  | IF:InteractiveFlag                << SetFlags (EFI_IFR_FLAG_INTERACTIVE, IF->getLine());    >>
  | MF:ManufacturingFlag              << SetFlags (EFI_IFR_FLAG_MANUFACTURING, MF->getLine());  >>
  | DF:DefaultFlag                    << SetFlags (EFI_IFR_FLAG_DEFAULT, DF->getLine());        >>
  | NV:NVAccessFlag                   << SetFlags (EFI_IFR_FLAG_NV_ACCESS, NV->getLine());      >>
  | RR:ResetRequiredFlag              << SetFlags (EFI_IFR_FLAG_RESET_REQUIRED, RR->getLine()); >>
  | LC:LateCheckFlag                  << SetFlags (EFI_IFR_FLAG_LATE_CHECK, LC->getLine());     >>
  ;

dateTimeSubStatement :
  Prompt  "=" getStringId ","
  Help    "=" getStringId ","
                                      << WriteByte (0, 0); WriteWord (0); >> // bogus flags and key
  minMaxStepDefault                   << WriteMinMaxStepDefault (); >>
  ;
  
vfrStatementCheckBox :  
  << UINT32 LineNum, KeyValue = 0; ResetFlags (); >>
  IDCB:CheckBox                       << WriteOpByte (IDCB->getLine(), EFI_IFR_CHECKBOX_OP); >>
  VarId     "=" vfrStructFieldName[1] ","
  Prompt    "=" getStringId ","
  Help      "=" getStringId ","
  FF:Flags  "=" flagsField ( "\|" flagsField )*  "," << LineNum = FF->getLine(); >>
  { 
    Key "=" KV:Number  ","           << LineNum = KV->getLine(); KeyValue = GetNumber(KV->getText(), LineNum, 2); >>
  }
                                     << WriteFlagsKey (KeyValue, LineNum); >>
  EndCheckBox ";"
  ;
     
vfrStatementSubTitle :
  IDS:Subtitle Text "="               << WriteOpByte (IDS->getLine(), EFI_IFR_SUBTITLE_OP); >>
  getStringId                         // writes string indentifier
  ";"
  ;

//*****************************************************************************
//
// PARSE:
//    banner 
//      title = STRING_TOKEN(STR_BANNER_TITLE),
//      line  1,
//      align center;     // or left or right
//
//    banner, 
//      title = STRING_TOKEN(STR_BANNER_TITLE), timeout = 100;
//
vfrStatementBanner :
  IDB:Banner { "," }                    << WriteOpByte (IDB->getLine(), EFI_IFR_BANNER_OP); >>
  Title "=" getStringId ","
  ( 
    Line VAL:Number ","                 << WriteWord (GetNumber(VAL->getText(), VAL->getLine(), 2)); >>
    Align 
    ( Left                              << WriteByte (EFI_IFR_BANNER_ALIGN_LEFT, 0); >>
    | Center                            << WriteByte (EFI_IFR_BANNER_ALIGN_CENTER, 0); >>
    | Right                             << WriteByte (EFI_IFR_BANNER_ALIGN_RIGHT, 0); >>
    ) ";"
  |
    Timeout "=" TO:Number ";"           << WriteWord (GetNumber(TO->getText(), TO->getLine(), 2)); >>
                                        << WriteByte (EFI_IFR_BANNER_TIMEOUT, 0); >>
  )
  ;

//*****************************************************************************
//
// PARSE:
//   oneof  varid       = MyNv.OneOfData,
//          prompt      = STRING_TOKEN(STR_ONE_OF_PROMPT),
//          help        = STRING_TOKEN(STR_ONE_OF_HELP),
//          option text = STRING_TOKEN(STR_ONE_OF_TEXT), 
//          value       = 0, 
//          flags       = DEFAULT | INTERACTIVE;
//
// supressif/grayoutif are supported inside oneof stmt.
// We do not restrict the number of oneOfOptionText to >=2, but >=1.
// The situation that all oneOfOptionText are suppressed is also possiable.
//
vfrStatementOneOf :
  << ResetFlags (); >>
  IDOO:OneOf                              << WriteOpByte (IDOO->getLine(), EFI_IFR_ONE_OF_OP); >>
  VarId   "=" vfrStructFieldName[2] ","       
  Prompt  "=" getStringId  ","           // writes string identifier
  Help    "=" getStringId  ","           // writes string identifier
  ( oneOfOptionText )+                   // there must be at least 1 option to be choosed, not 2.
  IDEOO:EndOneOf   ";"                    << TestOneOfFlags (IDEOO->getLine()); WriteOpByte (IDEOO->getLine(), EFI_IFR_END_ONE_OF_OP); >>
  ;

//*****************************************************************************
//
// PARSE:
//  
//   orderedlist  varid       = MyNv.OrderedListData,
//                prompt      = STRING_TOKEN(STR_ORDERED_LIST_PROMPT),
//                help        = STRING_TOKEN(STR_ORDERED_LIST_HELP),  
//                option text = STRING_TOKEN(STR_ORDERED_LIST_TEXT), value = 0, flags = INTERACTIVE;
//                -- additional option text -- 
//   endlist;
//
vfrStatementOrderedList :
  << ResetFlags (); InitOrderedList(); >>
  IDOL:OrderedList                       << WriteOpByte (IDOL->getLine(), EFI_IFR_ORDERED_LIST_OP); >>
  VarId   "=" vfrStructFieldNameArray[1] ","       
  Prompt  "=" getStringId  ","           // writes string identifier
  Help    "=" getStringId  ","           // writes string identifier
  orderedListOptionText ( orderedListOptionText )+
  IDEOL:EndList   ";"                    << WriteOpByte (IDEOL->getLine(), EFI_IFR_END_OP); EndOrderedList(IDEOL->getLine()); >>
  ;

//*****************************************************************************
//
// PARSE:
//
//   option text = STRING_TOKEN(STRING_ID), value = 0 flags = 99;
//
// Differs from the oneOfOptionText in that we don't allow the DEFAULT flag to
// be set, and value cannot be 0.
//
orderedListOptionText :
  << UINT32 KeyValue = 0; >>
  IDO:Option                          << WriteOpByte (IDO->getLine(), EFI_IFR_ONE_OF_OPTION_OP); >>
  Text      "=" getStringId ","       // writes string identifier
  Value     "=" WVAL:Number ","       << 
                                          if (GetNumber(WVAL->getText(), WVAL->getLine(), 2) == 0) {
                                            PrintErrorMessage (WVAL->getLine(), "value=0 is invalid for ordered lists", NULL); 
                                          } else {
                                            WriteWord (GetNumber(WVAL->getText(), WVAL->getLine(), 2)); 
                                          }
                                      >>
  FF:Flags  "=" orderedListFlagsField  
                ("\|" orderedListFlagsField )*                   
  { 
    "," Key "=" KV:Number             << KeyValue = GetNumber (KV->getText(), KV->getLine(), 2); >> 
  }
                                      << WriteFlagsKey (KeyValue, FF->getLine()); >>
  ";"                                 << mOptionCount++; >>
  ;

//*****************************************************************************
//
// PARSE:
//
//   flags = 0x10 | DEFAULT | MANUFACTURING | INTERACTIVE | NV_ACCESS | RESET_REQUIRED | LATE_CHECK
//
// The ordered list flags field cannot have a default.
//
orderedListFlagsField :
  VAL:Number                          << SetFlags (GetNumber(VAL->getText(), VAL->getLine(), 4), VAL->getLine()); >>
  | IF:InteractiveFlag                << SetFlags (EFI_IFR_FLAG_INTERACTIVE, IF->getLine());    >>
  | MF:ManufacturingFlag              << SetFlags (EFI_IFR_FLAG_MANUFACTURING, MF->getLine());  >>
  | NV:NVAccessFlag                   << SetFlags (EFI_IFR_FLAG_NV_ACCESS, NV->getLine());      >>
  | RR:ResetRequiredFlag              << SetFlags (EFI_IFR_FLAG_RESET_REQUIRED, RR->getLine()); >>
  | LC:LateCheckFlag                  << SetFlags (EFI_IFR_FLAG_LATE_CHECK, LC->getLine());     >>
  | DF:DefaultFlag                    << PrintWarningMessage (DF->getLine(), "DEFAULT flag not valid for ordered lists", NULL); >>
  ;

//
// Parse references to VFR structure field names of form "MyNvStructure.Field". 
// This implementation is specific to strings, passwords, and references in an 
// ordered list statement because we want to specify the size of the entire 
// field, rather than just one element. Then call a function to write out its 
// offset and length.
//
vfrStructFieldNameArray[int FieldWidth] :
  << int ArrayIndex = 1; char IsArrayIndex = 0; >>
  SName:StringIdentifier 
  "." 
  SFieldName:StringIdentifier 
  { OpenBracket AIndex:Number CloseBracket << ArrayIndex = GetNumber(AIndex->getText(), AIndex->getLine(), 4); IsArrayIndex = 1; >> }
            << 
                WriteFieldOffset (1, 
                                  SName->getText(), 
                                  SName->getLine(), 
                                  SFieldName->getText(), 
                                  SFieldName->getLine(),
                                  ArrayIndex, 
                                  IsArrayIndex,
                                  FieldWidth,
                                  1
                                  ); 
            >>
  ;

//
// Parse references to VFR structure field names of form "MyNvStructure.Field",
// then call a function to write out its offset and length.
//
vfrStructFieldName[int FieldWidth] :
  << int ArrayIndex = 1; char IsArrayIndex = 0; >>
  SName:StringIdentifier 
  "." 
  SFieldName:StringIdentifier 
  { OpenBracket AIndex:Number CloseBracket << ArrayIndex = GetNumber(AIndex->getText(), AIndex->getLine(), 4); IsArrayIndex = 1; >> }
            << 
                WriteFieldOffset (1, 
                                  SName->getText(), 
                                  SName->getLine(), 
                                  SFieldName->getText(), 
                                  SFieldName->getLine(),
                                  ArrayIndex, 
                                  IsArrayIndex,
                                  FieldWidth,
                                  0
                                  ); 
            >>
  ;

//*****************************************************************************
//
// PARSE:
//
//   MyNvStructure.FieldName[4]
//
// Parse references to VFR structure field names of form "MyNvStructure.Field",
// then call a function to write out the offset with no length.
//
vfrStructFieldNameNL[int FieldWidth] :
  << int ArrayIndex = 1; char IsArrayIndex = 0; >>
  SName:StringIdentifier 
  "." 
  SFieldName:StringIdentifier 
  { OpenBracket AIndex:Number CloseBracket   << ArrayIndex = GetNumber(AIndex->getText(), AIndex->getLine(), 4); IsArrayIndex = 1; >> }
            << 
                WriteFieldOffset (0, 
                                  SName->getText(), 
                                  SName->getLine(), 
                                  SFieldName->getText(), 
                                  SFieldName->getLine(),
                                  ArrayIndex, 
                                  IsArrayIndex,
                                  FieldWidth,
                                  0
                                  ); 
            >>
  ;

//*****************************************************************************
//
// PARSE:
//   suppressif TRUE OR FALSE;
//   grayoutif FALSE OR TRUE;
//     option text = STRING_TOKEN(STRING_ID), value = 0 flags = 99;
//     option text = STRING_TOKEN(STRING_ID2), value = 1 flags = 98;
//   endif;
//
oneOfOptionText :
  suppressIfOptionText    |
  grayOutIfOptionText     |
  commonOptionText
  ;

suppressIfOptionText : 
  << ResetFlags (); >>
  OPID:SuppressIf                     << WriteOpByte (OPID->getLine(), EFI_IFR_SUPPRESS_IF_OP); SetIfStart (OPID->getLine()); >>
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* ","
  }
  << WriteFlags (); >> //  write the flags field 
  vfrBooleanExpression
  ";"
  { suppressIfGrayOutIf } ( commonOptionText )+
  ENDOP:EndIf ";"                     << WriteOpByte (ENDOP->getLine(), EFI_IFR_END_IF_OP); SetIfStart (0); >>
  ;

grayOutIfOptionText :
  << ResetFlags (); >>
  OPID:GrayOutIf                      << WriteOpByte (OPID->getLine(), EFI_IFR_GRAYOUT_IF_OP); SetIfStart (OPID->getLine()); >>
  { 
    FF:Flags  "=" flagsField ( "\|" flagsField )* "," 
  }
  << WriteFlags (); >> //  write the flags field
  vfrBooleanExpression
  ";"
  { grayoutIfSuppressIf } ( commonOptionText )+ 
  ENDOP:EndIf ";"                     << WriteOpByte (ENDOP->getLine(), EFI_IFR_END_IF_OP); SetIfStart (0); >>
  ;

commonOptionText : 
  << UINT32 KeyValue = 0; >>
  IDO:Option                      << WriteOpByte (IDO->getLine(), EFI_IFR_ONE_OF_OPTION_OP); >>
  Text      "=" getStringId ","   // writes string identifier
  Value     "=" WVal:Number ","   << WriteWord (GetNumber(WVal->getText(), WVal->getLine(), 2)); >>
  FF:Flags  "=" flagsField  ("\|" flagsField )*                   
  { 
    "," Key "=" KV:Number         << KeyValue = GetNumber (KV->getText(), KV->getLine(), 2); >> 
  }
                                  << WriteFlagsKey (KeyValue, FF->getLine()); >>
  ";"                             << mOptionCount++; >>
  ;

//
// Gets a string identifier. It must be a numeric value of form:
// 
//   STRING_TOKEN(100)
//
getStringId :
  << unsigned short StrId; >>
  StringToken OpenParen
  IdVal:Number             << StrId = GetNumber (IdVal->getText(), IdVal->getLine(), 2); WriteStringIdWord (StrId); >> 
  CloseParen
  ;

//******************************************************************************
//
// Parser class definition. 
//  
class EfiVfrParser {
<<
//
// Parser definitions go here    
//
private:
  STRUCT_DEFINITION   *mFirstStructDefinition;
  STRUCT_DEFINITION   *mLastStructDefinition;
  INT32               mNvDataStructSize;                    
  INT32               mNonNvDataStructSize;
  //
  // Flag to indicate that we're processing a ideqid VFR statement so that
  // we can do late checks on the statement.
  //
  INT32               mIdEqIdStmt;
  INT32               mLastNVVariableDataSize;
  GOTO_REFERENCE      *mGotoReferences;
  FORM_ID_VALUE       *mFormIdValues;
  VfrOpcodeHandler    mOpcodeHandler;
  UINT16_LIST         *mUint16List;
  UINT16_LIST         *mLastUint16;
  UINT16_LIST         *mDefinedLabels;
  UINT16_LIST         *mDefinedVarStoreId;
  UINT16_LIST         *mLastDefinedVarStoreId;
  UINT32              mMinimumValue, mMaximumValue, mStepValue, mDefaultValue;
  UINT32              mStmtFlags;
  UINT32              mSubStmtFlags;
  UINT32              mSubStmtFlagsLineNum;
  EFI_GUID            mFormSetGuid;
  UINT8               mNvDataStructDefined;
  UINT16              mClass, mSubclass;
  UINT32              mIfStart;
  UINT32              mOptionCount;  // how many "option" fields in a given statement
  UINT32              mLastVarIdSize;
  UINT8               mOutput;
public:        

VOID 
SetIfStart (
  UINT32 LineNum
  )
/*++

Routine Description:
  Invoked during VFR parsing when an "if" is encountered. Save the
  source line number so we can point to it if we don't find a 
  corresponding endif later.

Arguments:
  LineNum - source line number where the "if" was parsed.

Returns:
  None

--*/
{
  mIfStart = LineNum;
}
VOID 
SetClass (
  UINT32 LineNum, 
  UINT32 Value
  ) 
/*++

Routine Description:
  Invoked during VFR parsing when a "class" statement is found. Check the
  range on the class value and save it for later.

Arguments:
  LineNum - source line number where the class statement was parsed.
  Value   - the class value

Returns:
  None

--*/
{
  if (Value & 0xFFFF0000) {
    PrintWarningMessage (LineNum, NULL, "class value exceeds maximum allowed");
  }
  mClass |= (UINT16)Value;
}
VOID 
SetSubclass (
  UINT32 LineNum, 
  UINT32 Value
  ) 
/*++

Routine Description:
  Invoked during VFR parsing when a subclass statement is found. Check the
  range on the value and save it for later.

Arguments:
  LineNum - source line number where the class statement was parsed.
  Value   - the subclass value from the VFR statement

Returns:
  None

--*/
{
  if (Value & 0xFFFF0000) {
    PrintWarningMessage (LineNum, NULL, "subclass value exceeds maximum allowed");
  }
  mSubclass |= (UINT16)Value;
}
VOID WriteClass ()
{
  WriteWord (mClass);
  mClass = 0;
}
VOID WriteSubclass ()
{
  WriteWord (mSubclass);
  mSubclass = 0;
}
VOID WriteIfrBytes ()
{
  mOpcodeHandler.WriteIfrBytes ();
}
VOID 
WriteFlagsKey (
  UINT32 KeyValue, 
  UINT32 LineNum
  ) 
/*++

Routine Description:
  Write out the flags and key values from the previous VFR statement.
  Many statements take a flags/key pair. If not specified, then 0
  values are written out. However do not allow an interactive flags field
  to be specified if no key value is specified. Also, if NV_ACCESS flag
  is set but INTERACTIVE is not, then set interactive and issue a warning.

Arguments:
  KeyValue  - the key value from the VFR statement
  LineNum   - source line number where the statement was parsed

Returns:
  None

--*/
{
  if ((mSubStmtFlags & EFI_IFR_FLAG_INTERACTIVE) && (KeyValue == 0)) {
    PrintErrorMessage (LineNum, NULL, "invalid or missing key value - required with INTERACTIVE");
  }
  if ((mSubStmtFlags & EFI_IFR_FLAG_NV_ACCESS) && !(mSubStmtFlags & EFI_IFR_FLAG_INTERACTIVE)) {
    PrintWarningMessage (LineNum, NULL, "NV_ACCESS without INTERACTIVE has no effect -- setting INTERACTIVE");
    mSubStmtFlags |= EFI_IFR_FLAG_INTERACTIVE;
  }
  WriteFlags ();
  WriteWord (KeyValue);
}
VOID 
InitOrderedList ()
{
  mOptionCount = 0;
}  
VOID 
EndOrderedList (
  UINT32 LineNum
  )
{
  if (mLastVarIdSize < mOptionCount) {
    PrintErrorMessage (LineNum, NULL, "number of options exceeds the variable store size");
  }
}
VOID 
ResetFlags ()
/*++

Routine Description:

  Flags are set for each substatement in a given one-of statement.
  To make sure there are no conflicts, for example setting DEFAULT on
  more than one substatement, we keep track of the flags at a statement
  level and a substatement level. This function resets the flags so 
  we get a fresh start.

Arguments:
  None

Returns:
  None

--*/
{
  mStmtFlags = 0;
  mSubStmtFlagsLineNum = 0;
  mSubStmtFlags = 0;
}
//
// Test validity of flags value for a one-of statement.
//
VOID 
TestOneOfFlags (
  UINT32 LineNum
  ) 
{
  //
  // One of the fields must have had the default bit set
  //
  if ((mStmtFlags & EFI_IFR_FLAG_DEFAULT) == 0) {
    PrintWarningMessage (LineNum, "default value must be specified", NULL);
  }
}
VOID 
SetFlags (
  UINT32 Flags, 
  UINT32 LineNum
  ) 
{
  //
  // Check for redefinitions and invalid combinations
  //
  if (mStmtFlags & Flags & EFI_IFR_FLAG_MANUFACTURING) {
    PrintErrorMessage (LineNum, "MANUFACTURING", "a field with this flag already defined");
  }
  if (mStmtFlags & Flags & EFI_IFR_FLAG_DEFAULT) {
    PrintErrorMessage (LineNum, "DEFAULT", "a field with this flag already defined");
  }
  mSubStmtFlags |= Flags;
  mSubStmtFlagsLineNum = LineNum;
}
VOID 
WriteFlags ()
{
  //
  // Check value for validity
  //
  if (mSubStmtFlags & ~(EFI_IFR_FLAG_DEFAULT | 
                        EFI_IFR_FLAG_MANUFACTURING | 
                        EFI_IFR_FLAG_INTERACTIVE | 
                        EFI_IFR_FLAG_NV_ACCESS | 
                        EFI_IFR_FLAG_RESET_REQUIRED | 
                        EFI_IFR_FLAG_LATE_CHECK )) {
    PrintWarningMessage (mSubStmtFlagsLineNum, "invalid bits defined in flag", NULL);
  }
  WriteByte ((UINT8)mSubStmtFlags, 'F');
  //
  // We can now clear the substatement flags
  //
  mStmtFlags |= mSubStmtFlags;
  mSubStmtFlags = 0;
}
//
// When we parse a min/max/step/default sequence, save off the values for
// later use. Call this first to init the values.
//
VOID 
InitMinMaxStepDefault ()
{
  mMinimumValue         = 0;
  mMaximumValue         = 0;
  mStepValue            = 1;
  mDefaultValue         = 0;
}  
VOID 
WriteMinMaxStepDefault ()
{
  WriteWord (mMinimumValue);
  WriteWord (mMaximumValue);
  WriteWord (mStepValue);
  WriteWord (mDefaultValue);
}  
VOID 
SetMinMaxStepDefault (
  UINT16  Value, 
  INT32   MMSD, 
  INT32   LineNum
  ) 
{
  UINT16 TempValue;
  //
  // Min specified
  //
  if (MMSD == 0) {
    mMinimumValue = Value;
    mDefaultValue = Value;
  //
  // Max specified
  //
  } else if (MMSD == 1) {
    mMaximumValue = Value;
    //
    // If min > max, then swap the values. That includes resetting the default
    // value as well.
    //
    if (mMinimumValue > mMaximumValue) {
      PrintWarningMessage (LineNum, NULL, "maximum < minimum");      
      TempValue = Value;
      mMaximumValue = mMinimumValue;
      mMinimumValue = TempValue;
      mDefaultValue = mMinimumValue;
    }
  //
  // Step specified
  //
  } else if (MMSD == 2) { 
    mStepValue = Value;
  //
  // Default specified. Make sure min <= default <= max.
  //
  } else if (MMSD == 3) {
    mDefaultValue = Value;
    if (mMinimumValue > Value) {
      PrintErrorMessage (LineNum, NULL, "default value < minimum value");
    } else if (Value > mMaximumValue) {
      PrintErrorMessage (LineNum, NULL, "default value > maximum value");
    }
  } else {
    PrintErrorMessage (LineNum, "application error", "internal MMSD error");    
  }
}
VOID 
AddLabel (
  UINT32 LabelNumber, 
  UINT32 LineNum
  ) 
{
  UINT16_LIST *Label;

  //
  // Added a label from the user VFR script. Make sure they haven't already 
  // defined the same label elsewhere
  //
  for (Label = mDefinedLabels; Label != NULL; Label = Label->Next) {
    if (Label->Value == LabelNumber) {
      PrintErrorMessage (LineNum, NULL, "label already defined");
      PrintErrorMessage (Label->LineNum, NULL, "previous definition of redefined label");
      break;
    }
  }
  Label = (UINT16_LIST *)malloc (sizeof (UINT16_LIST));
  if (Label == NULL) {
    PrintErrorMessage (0, NULL, "memory allocation error");
    return;
  }
  memset ((char *)Label, 0, sizeof (UINT16_LIST));
  Label->Value = LabelNumber;
  Label->LineNum = LineNum;
  Label->Next = mDefinedLabels;
  mDefinedLabels = Label;
}
VOID 
QueueIdEqValList (
  UINT16 Value
  )
{
  UINT16_LIST   *U16;
  
  U16 = (UINT16_LIST *)malloc (sizeof (UINT16_LIST));
  if (U16 == NULL) {
    Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failed");
  } else {
    memset ((char *)U16, 0, sizeof (UINT16_LIST));
    U16->Value = Value;
    if (mUint16List == NULL) {
      mUint16List = U16;
    } else {
      mLastUint16->Next = U16;
    } 
    mLastUint16 = U16;
  }
}    
VOID 
FlushQueueIdEqValList ()
{
  UINT32 Count;
  
  //
  // We queued up a list of IdEqValList items. The IFR requires a count
  // followed by the actual values. Do it.
  //
  Count = 0;
  mLastUint16 = mUint16List;
  while (mLastUint16 != NULL) {
    Count++;
    mLastUint16 = mLastUint16->Next;
  }
  // BUGBUG -- check for more than 16K items?
  WriteWord (Count);
  //
  // Now write the values.
  //
  mLastUint16 = mUint16List;
  while (mLastUint16 != NULL) {
    WriteWord ((UINT32)mLastUint16->Value);
    mLastUint16 = mLastUint16->Next;
  }
  //
  // Free up the list
  //  
  mLastUint16 = mUint16List;
  while (mUint16List != NULL) {
    mLastUint16 = mUint16List->Next;
    free (mUint16List);
    mUint16List = mLastUint16;
  }
}
VOID 
PrintErrorMessage (
  UINT32              LineNum,
  INT8                *Msg1,
  INT8                *Msg2
  )
{
  char *FileName;
  
  if (LineNum != 0) {
    FileName = ConvertLineNumber ((UINT32 *)&LineNum);
    Error (FileName, LineNum, 0, Msg1, Msg2);
  } else {
    Error (PROGRAM_NAME, 0, 0, Msg1, Msg2);
  }
}
VOID 
PrintWarningMessage (
  UINT32              LineNum,
  INT8                *Msg1,
  INT8                *Msg2
  )
{
  char *FileName;
  
  if (LineNum != 0) {
    FileName = ConvertLineNumber ((UINT32 *)&LineNum);
    Warning (FileName, LineNum, 0, Msg1, Msg2);
  } else {
    Warning (PROGRAM_NAME, 0, 0, Msg1, Msg2);
  }
}
VOID 
syn (
  ANTLRAbstractToken  *Tok, 
  ANTLRChar           *Egroup, 
  SetWordType         *Eset, 
  ANTLRTokenType      ETok, 
  INT32               Huh
  )
/*++

Routine Description:
  Called by the parser base class as a result of parse syntax errors.

Arguments:
  Tok     - token that caused the error
  Egroup  - not sure
  Eset    - index in token table of the expected token
  Huh     - not sure

Returns:
  NA

--*/
{
  char    *FileName;
  UINT32  LineNum;
  
  LineNum = Tok->getLine ();
  FileName = ConvertLineNumber ((UINT32 *)&LineNum);
  //
  // Sometimes the token number is 0, in which case I don't know what to
  // print.
  //
  if (ETok == 0) {
    Error (FileName, LineNum, 0, Tok->getText (), "unexpected token");
  } else {
    //
    // If we were expecting an endif, then report the line where the corresponding
    // IF began.
    //
    if ((strcmp (_token_tbl[ETok], "endif") == 0) && (mIfStart != 0)) {
      LineNum = mIfStart;
      FileName = ConvertLineNumber (&LineNum);
      Error (FileName, LineNum, 0, "statement missing corresponding endif", NULL);
    } else {
      Error (FileName, LineNum, 0, Tok->getText (), "expected %s", _token_tbl[ETok]);
    }
  }
}

VOID 
init()        
/*++

Routine Description:
  Initializations function for our parser.

Arguments:
  None.

Returns:
  None.

--*/
{
  ANTLRParser::init();

  //
  // Used for queuing a variable list of UINT16's
  //
  mUint16List               = NULL;
  mLastUint16               = NULL;
  mFirstStructDefinition    = NULL;
  mLastStructDefinition     = NULL;
  mNvDataStructSize         = 0;
  mNonNvDataStructSize      = 0;
  mNvDataStructDefined      = 0;
  mGotoReferences           = NULL;
  mFormIdValues             = NULL;
  mDefinedLabels            = NULL;
  mClass                    = 0;
  mSubclass                 = 0;
  mIfStart                  = 0;
  mDefinedVarStoreId        = NULL;
  mLastDefinedVarStoreId    = NULL;
  mIdEqIdStmt               = 0;
  mLastNVVariableDataSize   = 0;
    
  memset ((char *)&mFormSetGuid, 0, sizeof (EFI_GUID));
}
//
// Destructor for the parser.
//
~EfiVfrParser(VOID)
{
  Cleanup();
}
VOID
Cleanup (VOID)
/*++

Routine Description:
  Free memory allocated during parsing

Arguments:
  None.

Returns:
  None.

--*/
{
  STRUCT_DEFINITION         *NextStruct;
  STRUCT_FIELD_DEFINITION   *NextField;
  UINT8                     Buff[6];
  UINT16_LIST               *NextU16List;
  
  //
  // Free up the structure definitions if any
  //
  while (mFirstStructDefinition != NULL) {
    //
    // Free up all the fields for this struct
    //
    while (mFirstStructDefinition->Field != NULL) {
      NextField = mFirstStructDefinition->Field->Next;
      free (mFirstStructDefinition->Field->Name);
      free (mFirstStructDefinition->Field);
      mFirstStructDefinition->Field = NextField;
    }
    NextStruct = mFirstStructDefinition->Next;
    free (mFirstStructDefinition->Name);
    free (mFirstStructDefinition);
    mFirstStructDefinition = NextStruct;
  }
  //
  // Free up the goto references and form id defines
  //
  FreeGotoReferences ();
  //
  // Free up label list
  //
  while (mDefinedLabels != NULL) {
    NextU16List = mDefinedLabels->Next;
    delete (mDefinedLabels);
    mDefinedLabels = NextU16List;
  }
  //
  // Free up the list of defined variable storage IDs
  //
  while (mDefinedVarStoreId != NULL) {
    NextU16List = mDefinedVarStoreId->Next;
    delete (mDefinedVarStoreId);
    mDefinedVarStoreId = NextU16List;
  }
}

INT32 
AtoX (
  INT8    *HexString, 
  INT32   NumBytes, 
  UINT32  *HexValue
  )
/*++

Routine Description:
  Given a pointer to a ascii hex string, convert to a number with the given
  number of bytes.

Arguments:
  HexString   - pointer to a string of format 30BCA
  Size        - number of bytes to convert
  HexValue    - return result

Returns:
  The number of bytes converted.

--*/
{
  INT32 Count;
  INT32 Value;

  *HexValue = 0;
  Count = 0;
  while (Count < NumBytes) {
    if ((*HexString >= '0') && (*HexString <= '9')) {
      Value = *HexString - '0';
    } else if ((*HexString >= 'a') && (*HexString <= 'f')) {
      Value = *HexString - 'a' + 10;
    } else if ((*HexString >= 'A') && (*HexString <= 'F')) {
      Value = *HexString - 'A' + 10;
    } else {
      return Count;
    }
    HexString++;
    *HexValue = (*HexValue << 4) | Value;
    if ((*HexString >= '0') && (*HexString <= '9')) {
      Value = *HexString - '0';
    } else if ((*HexString >= 'a') && (*HexString <= 'f')) {
      Value = *HexString - 'a' + 10;
    } else if ((*HexString >= 'A') && (*HexString <= 'F')) {
      Value = *HexString - 'A' + 10;
    } else {
      return Count;
    }
    *HexValue = (*HexValue << 4) | Value;
    HexString++;
    Count++;
  }
  return Count;
}
VOID 
WriteGuidValue (
  UINT32       TokenLineNum,
  INT8         *G1, 
  INT8         *G2,
  INT8         *G3,
  INT8         *G4,
  INT8         *G5,
  INT8         *G6,
  INT8         *G7,
  INT8         *G8,
  INT8         *G9,
  INT8         *G10,
  INT8         *G11
  )
/*++

Routine Description:
  A Guid was parsed, likely of format:
  #define MY_GUID { 0x12345678, 0xAABB, 0xCCDD, 0xEE, 0xFF, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }

  Write out the value.

Arguments:
  TokenLineNum   - line number where the guid was used
  G1-G11         - the 11 fields of the guid value

Returns:
  None.

--*/
{
  UINT32        Value;
  INT32         Loop;
  INT8          *Cptr;

  mFormSetGuid.Data1 = GetNumber (G1, TokenLineNum, 4);
  mFormSetGuid.Data2 = (UINT16)GetNumber (G2, TokenLineNum, 2);
  mFormSetGuid.Data3 = (UINT16)GetNumber (G3, TokenLineNum, 2);
  mFormSetGuid.Data4[0] = (UINT8)GetNumber (G4, TokenLineNum, 1);
  mFormSetGuid.Data4[1] = (UINT8)GetNumber (G5, TokenLineNum, 1);
  mFormSetGuid.Data4[2] = (UINT8)GetNumber (G6, TokenLineNum, 1);
  mFormSetGuid.Data4[3] = (UINT8)GetNumber (G7, TokenLineNum, 1);
  mFormSetGuid.Data4[4] = (UINT8)GetNumber (G8, TokenLineNum, 1);
  mFormSetGuid.Data4[5] = (UINT8)GetNumber (G9, TokenLineNum, 1);
  mFormSetGuid.Data4[6] = (UINT8)GetNumber (G10, TokenLineNum, 1);
  mFormSetGuid.Data4[7] = (UINT8)GetNumber (G11, TokenLineNum, 1);
  
  WriteDWord (mFormSetGuid.Data1, 'G');
  WriteWord (mFormSetGuid.Data2);
  WriteWord (mFormSetGuid.Data3);
  WriteByte (mFormSetGuid.Data4[0], 0);
  WriteByte (mFormSetGuid.Data4[1], 0);
  WriteByte (mFormSetGuid.Data4[2], 0);
  WriteByte (mFormSetGuid.Data4[3], 0);
  WriteByte (mFormSetGuid.Data4[4], 0);
  WriteByte (mFormSetGuid.Data4[5], 0);
  WriteByte (mFormSetGuid.Data4[6], 0);
  WriteByte (mFormSetGuid.Data4[7], 0);
}
VOID 
WriteFieldOffset (
  INT8    WriteLength,
  INT8    *StructName, 
  INT32   LineNum1, 
  INT8    *FieldName, 
  INT32   LineNum2,
  INT32   ArrayIndex,
  INT8    IsArrayIndex,
  INT32   FieldWidth,
  INT8    WriteArraySize
  ) 
/*++

Routine Description:
  A VFR script referenced the NV store structure. Given the structure's name
  and the field's name, write the offset of the field to the output file.

Arguments:
  WriteLength     - write the field length byte out
  StructName      - name of the NV store structure
  LineNum1        - line number in the VFR where we are (for error printing)
  FieldName       - the name of the field within the NV store structure
  LineNum2        - line number in the VFR where FieldName is referenced 
  ArrayIndex      - the index specified, for example NV_DATA.Field[ArrayIndex]
  IsArrayIndex    - non-zero if an array index was specified
  FieldWidth      - expected size for the Field (1 byte? 2 bytes?)
  WriteArraySize  - write the size of the entire field, not the size of a single element

Returns:
  None.

--*/
{
  STRUCT_DEFINITION         *StructDef;
  STRUCT_FIELD_DEFINITION   *FieldDef;
  UINT32                    Offset;
  UINT32                    VarSize;
  INT8                      Msg[100];
  //
  // If we're writing an array size, then they better have referenced the field without an
  // index. 
  //
  if (WriteArraySize && IsArrayIndex) {
    sprintf (Msg, "array index specified where an array is required");
    PrintErrorMessage (LineNum2, FieldName, Msg);
    return;
  }
  //
  // Look through our list of known structures for a match
  //
  for (StructDef = mFirstStructDefinition; StructDef != NULL; StructDef = StructDef->Next) {
    //
    // Check for matching structure name
    //
    if (strcmp (StructDef->Name, StructName) == 0) {
      //
      // Mark it as referenced (for debug purposes only). Check the
      // flag that indicates that we have already found a varstore VFR
      // statement for it.
      //
      StructDef->Referenced++;
      if (StructDef->VarStoreIdValid == 0) {
        //
        // Set it valid so we don't flag it multiple times, then emit the error
        //
        StructDef->VarStoreIdValid = 1;
        PrintErrorMessage (LineNum1, StructName, "varstore statement missing for this variable store");
      }
      //
      // Let the opcode-handler know which variable storage we're now using
      //
      if (mIdEqIdStmt) {
        mOpcodeHandler.SetSecondaryVarStoreId (StructDef->VarStoreId);
      } else {
        mOpcodeHandler.SetVarStoreId (StructDef->VarStoreId);
      }
      //
      // Found matching structure name. Now find the matching field name
      //
      for (FieldDef = StructDef->Field; FieldDef != NULL; FieldDef = FieldDef->Next) {
        if (strcmp (FieldDef->Name, FieldName) == 0) {
          //
          // Make sure the variable size is valid
          //
          if ((FieldWidth != 0) && (FieldDef->DataSize > FieldWidth)) {
            sprintf (Msg, "field width exceeds %d byte%c", FieldWidth, FieldWidth == 1 ? ' ' : 's');
            PrintErrorMessage (LineNum2, FieldName, Msg);
          }
          //
          // If they specified an index (MyVfrData.FieldX[10]), then make sure that the
          // data structure was declared as an array, and that the index is in bounds.
          // If they did not specify an index, then we'll assume 0. This is required for
          // strings.
          //
          if (IsArrayIndex) {
            VarSize = FieldDef->DataSize;
            if (FieldDef->IsArray == 0) {
              PrintErrorMessage (LineNum2, FieldName, "field is not declared as an array");
              return;
            }
            if (FieldDef->ArrayLength < ArrayIndex) {
              PrintErrorMessage (LineNum2, FieldName, "array index exceeds declared size of field");
              return;
            }
          } else {
            if (FieldDef->IsArray) {
              VarSize = FieldDef->DataSize * FieldDef->ArrayLength;
            } else {
              VarSize = FieldDef->DataSize;
            }
          }
          //
          // If we're in the middle of a ideqid VFR statement, then this is the second
          // variable ID that we're now processing. Make sure that its size is the same
          // as the first variable.
          // 
          if (mIdEqIdStmt) {
            if (mLastVarIdSize != VarSize) {
              PrintErrorMessage (LineNum2, FieldName, "variables must have the same size");
              return;
            }
          }
          mLastVarIdSize = VarSize;
          //
          // If we're supposed to write an array size, then require it to be an array
          //
          if (WriteArraySize && !FieldDef->IsArray) {
            PrintErrorMessage (LineNum2, FieldName, "array required");
            return;
          }
          //
          // Write the variable offset and size. If we're in the non-NV structure, then
          // set the offset beyond the NV data structure size.
          //
          Offset = FieldDef->Offset + FieldDef->DataSize * (ArrayIndex - 1);
          if (StructDef->IsNonNV) Offset += mNvDataStructSize; 
          WriteWord (Offset);
          if (WriteLength) {
            if (WriteArraySize) {
              if (FieldDef->DataSize * FieldDef->ArrayLength > 255) {
                PrintErrorMessage (LineNum2, FieldName, "array size exceeds 255 maximum encoding limit");
                return;
              }
              WriteByte (FieldDef->DataSize * FieldDef->ArrayLength, 0);
            } else {
              WriteByte (FieldDef->DataSize, 0);
            }
          }
          return;
        }
      }
      sprintf (Msg, "structure %s does not have a field named '%s'", StructName, FieldName);
      PrintErrorMessage (LineNum2, Msg, NULL);
      PrintErrorMessage (StructDef->LineNum, "see structure definition", NULL);
      return;
    }
  }
  //
  // The structure was not found in the defined list. See if it's the "Date" structure
  //
  if (strcmp (StructName, "Date") == 0) {
    //
    // BUGBUG -- remove support for Date and Time as valid structure 
    // names. They should use the NON_NV_DATA_MAP structure for this.
    //
    // Someone specified Date.Years|Months|Days
    // BUGBUG -- define some constants for the IDs used here
    // Length == 0 implies that this is not user NV data storage.
    //
    if (strcmp (FieldName, "Year") == 0) {
      //
      // Write ID (offset), ID, and size
      //
      WriteWord (mNvDataStructSize + mNonNvDataStructSize + 0);
      if (WriteLength) {
        WriteByte (0, 0);
      }
    } else if (strcmp (FieldName, "Month") == 0) {
      //
      // Write ID (offset), ID, and size
      //
      WriteWord (mNvDataStructSize + mNonNvDataStructSize + 2);
      if (WriteLength) {
        WriteByte (0, 0);
      }
    } else if (strcmp (FieldName, "Day") == 0) {
      //
      // Write ID (offset), ID, and size
      //
      WriteWord (mNvDataStructSize + mNonNvDataStructSize + 4);
      if (WriteLength) {
        WriteByte (0, 0);
      }
    } else {
      PrintErrorMessage (LineNum1, FieldName, "expected valid field name TheYear/TheMonth/TheDay");
    }
    return;
  } else if (strcmp (StructName, "Time") == 0) {
    //
    // Someone specified Time.Hours|Minutes|Seconds
    // BUGBUG -- define some constants for the IDs used here
    //
    if (strcmp (FieldName, "Hours") == 0) {
      //
      // Write ID (offset), ID, and size
      //
      WriteWord (mNvDataStructSize + mNonNvDataStructSize + 6);
      if (WriteLength) {
        WriteByte (0, 0);
      }
    } else if (strcmp (FieldName, "Minutes") == 0) {
      //
      // Write ID (offset), ID, and size
      //
      WriteWord (mNvDataStructSize + mNonNvDataStructSize + 8);
      if (WriteLength) {
        WriteByte (0, 0);
      }
    } else if (strcmp (FieldName, "Seconds") == 0) {
      //
      // Write ID (offset), ID, and size
      //
      WriteWord (mNvDataStructSize + mNonNvDataStructSize + 10);
      if (WriteLength) {
        WriteByte (0, 0);
      }
    } else {
      PrintErrorMessage (LineNum1, FieldName, "expected valid field name Hours/Minutes/Seconds");
    }
    return;
  } else {
    PrintErrorMessage (LineNum1, StructName, "undefined structure");
    return;
  }
}
VOID
StartStructDefinition (
  INT32  IsNonNV,
  INT32  LineNum
  )
/*++

Routine Description:
  Called when we encounter a new "struct _MY_STRUCT..." statement while parsing. 
  Initialize internal data and structures for parsing the fields of the structure.

Arguments:
  LineNum  - line number in the source file (for error reporting purposes)
  IsNonNv  - flag indicating (if nonzero) that the variable referred to is not in
             the standard NV store.
Returns:
  None

--*/
{
  STRUCT_DEFINITION *StructDef;
  //
  // Allocate memory for the structure record
  //
  StructDef = (STRUCT_DEFINITION *)malloc (sizeof (STRUCT_DEFINITION));
  memset (StructDef, 0, sizeof (STRUCT_DEFINITION));
  StructDef->LineNum = LineNum;
  //
  // Set flag indicating non-NV data structure or not
  //
  StructDef->IsNonNV = IsNonNV;
  //
  // Add it to the end of our linked list. If it's the first one
  // defined, then it's the default varstore ID, so set it valid.
  //
  if (mFirstStructDefinition == NULL) {
    mFirstStructDefinition = StructDef;
    StructDef->VarStoreIdValid = 1;
  } else {
    mLastStructDefinition->Next = StructDef;
  }
  mLastStructDefinition = StructDef;
}
VOID
EndStructDefinition (
  INT8   *StructName,
  INT32  LineNum
  )
{
  STRUCT_DEFINITION         *StructDef;
  STRUCT_FIELD_DEFINITION   *FieldDef;
  UINT32                    Offset;
  //
  // Make sure they have not already defined a structure with this name
  //
  for (StructDef = mFirstStructDefinition; StructDef != NULL; StructDef = StructDef->Next) {
    if ((StructDef->Name != NULL) && (strcmp (StructDef->Name, StructName) == 0)) {
      PrintErrorMessage (LineNum, StructName, "structure with this name already defined");
      //
      // Fall through and fill in the rest of the structure information. We do
      // this because the structure has already been added to our global list,
      // so will be used elsewhere, so we want it to contain valid fields.
      //
    }
  }    
  //
  // Allocate memory for the structure name 
  //
  mLastStructDefinition->Name = (char *)malloc (strlen (StructName) + 1);
  strcpy (mLastStructDefinition->Name, StructName);
  //
  // Compute the structure size, and the offsets to each field
  //
  Offset = 0;
  for (FieldDef = mLastStructDefinition->Field; FieldDef != NULL; FieldDef = FieldDef->Next) {
    FieldDef->Offset = Offset;
    Offset += FieldDef->ArrayLength * FieldDef->DataSize;
  }
  mLastStructDefinition->Size = Offset;
  //
  // Go through all the structure we have so far and figure out (if we can)
  // the size of the non-NV storage. We also assume that the first structure
  // definition is the primary/default storage for the VFR form.
  //
  if (mNonNvDataStructSize == 0) {
    for (StructDef = mFirstStructDefinition; StructDef != NULL; StructDef = StructDef->Next) {
      if (StructDef->IsNonNV) {
        mNonNvDataStructSize = StructDef->Size;
        break;
      }
    }
  }
  if (mNvDataStructSize == 0) {
    for (StructDef = mFirstStructDefinition; StructDef != NULL; StructDef = StructDef->Next) {
      if (StructDef->IsNonNV == 0) {
        mNvDataStructSize = StructDef->Size;
        break;
      }
    }
  }
}
VOID 
AddStructField (
  INT8    *FieldName, 
  INT32   LineNum, 
  INT32   DataSize,
  INT32   ArrayLength,
  INT8    IsArray
  ) 
/*++

Routine Description:
  We're parsing the VFR structure definition. Add another defined field to 
  our definition.

Arguments:
  FieldName   - name of the field in the structure.
  LineNum     - the line number from the input (preprocessor output) file
  DataSize    - the size of the field (1, 2, or 4 bytes)
  ArrayLength - the number of elements (for array)
  IsArray     - non-zero if the field is an array

Returns:
  None.

--*/
{
  STRUCT_FIELD_DEFINITION *FieldDef;
  STRUCT_FIELD_DEFINITION *Temp;
  //
  // Make sure we don't already have a field of this name in our structure
  //
  for (FieldDef = mLastStructDefinition->Field; FieldDef != NULL; FieldDef = FieldDef->Next) {
    if (strcmp (FieldDef->Name, FieldName) == 0) {
      PrintErrorMessage (LineNum, FieldName, "field with this name already defined");
      return;
    }
  } 
  //
  // If it's an array, then they better not have a size of 0. For example:
  //   UINT8 MyBytes[0];
  //
  if (IsArray && (ArrayLength <= 0)) {
    PrintErrorMessage (LineNum, FieldName, "invalid array size");
    return;
  }    
  //
  // Allocate memory for a new structure field definition
  //    
  FieldDef = (STRUCT_FIELD_DEFINITION *)malloc (sizeof (STRUCT_FIELD_DEFINITION));
  memset ((char *)FieldDef, 0, sizeof (STRUCT_FIELD_DEFINITION));
  FieldDef->ArrayLength  = ArrayLength;
  FieldDef->DataSize     = DataSize;
  FieldDef->IsArray      = IsArray;
  FieldDef->Name = (char *)malloc (strlen (FieldName) + 1);
  strcpy (FieldDef->Name, FieldName);
  //
  // Add it to the end of the field list for the currently active structure
  //
  if (mLastStructDefinition->Field == NULL) {
    mLastStructDefinition->Field = FieldDef;
  } else {
    mLastStructDefinition->LastField->Next = FieldDef;
  }
  mLastStructDefinition->LastField = FieldDef;
}
VOID
AddVarStore (
  INT8   *StructName,       // actual name of the structure
  INT8   *VarName,          // actual NV variable name
  UINT16 VarStoreId,        // key value
  INT32  LineNum            // parse line number (for error reporting)
  )
/*++

Routine Description:
  Called while parsing a varstore statement. Add the variable store 
  to our linked list.

Arguments:
  StructName    - the name of the typedef'ed structure to use
  VarName       - the NV variable name as specified in the varstore statement
  VarStoreId    - the variable store ID as specified in the varstore statememt
  LineNum       - the line number from the input (preprocessor output) file

Returns:
  None.

--*/
{
  STRUCT_DEFINITION *StructDef;
  UINT16_LIST       *L16Ptr;
  //
  // Go through our list of previously-defined variable store IDs and
  // make sure this one is not a duplicate in name or key value.
  //
  for (L16Ptr = mDefinedVarStoreId; L16Ptr != NULL; L16Ptr = L16Ptr->Next) {
    if (L16Ptr->Value == VarStoreId) {
      PrintErrorMessage (LineNum, "variable storage key already used", NULL);
      PrintErrorMessage (L16Ptr->LineNum, "previous usage of storage key", NULL);
    }
  }
  // 
  // Key value of 0 is invalid since that's assigned by default to the default
  // variable store (the first structure parsed).
  //
  if (VarStoreId == 0) {
    PrintErrorMessage (LineNum, "variable storage key of 0 is invalid", NULL);
  }
  //
  // Create a new element to add to the list
  //
  L16Ptr = (UINT16_LIST *)malloc(sizeof (UINT16_LIST));
  memset (L16Ptr, 0, sizeof (UINT16_LIST));
  L16Ptr->LineNum = LineNum;
  L16Ptr->Value = VarStoreId;
  if (mDefinedVarStoreId == NULL) {
    mDefinedVarStoreId = L16Ptr;
  } else {
    mLastDefinedVarStoreId->Next = L16Ptr;
  }
  mLastDefinedVarStoreId = L16Ptr;
  //
  // Find the structure definition with this name
  //
  for (StructDef = mFirstStructDefinition; StructDef != NULL; StructDef = StructDef->Next) {
    if (strcmp (StructDef->Name, StructName) == 0) {
      //
      // Make sure they did not already define a variable storage ID 
      // for this structure.
      //
      if (StructDef->VarStoreId != 0) {
        PrintErrorMessage (LineNum, StructName, "variable storage already defined for this structure");
        PrintErrorMessage (StructDef->VarStoreLineNum, StructName, "previous definition for variable storage");
      }
      StructDef->VarStoreId       = VarStoreId;
      StructDef->VarStoreIdValid  = 1;
      StructDef->VarStoreLineNum  = LineNum;
      WriteWord (StructDef->Size);
      while (*VarName) {
        WriteByte(*VarName, 0);
        VarName++;
      }
      WriteByte(0,0);
      return;
    }
  }    
  PrintErrorMessage (LineNum, StructName, "structure with this name not defined");
}
VOID 
WriteDWord (
  UINT32    Value, 
  UINT8     KeyByte
  )
/*++

Routine Description:
  During parsing, we came upon some code that requires a 32-bit value be
  written to the VFR binary file. Queue up the 4 bytes.

Arguments:
  Value   - the 32-bit value to write
  KeyByte - a single character which gets written out beside the first byte.
            This is used to tag the data in the output file so that during 
            debug you have an idea what the value is.

Returns:
  None.

--*/
{
  //
  // Write 4 bytes, little endian. Specify a key byte only on the first one
  //
  mOpcodeHandler.AddByte ((UINT8)Value, KeyByte);
  Value \>>= 8;
  mOpcodeHandler.AddByte ((UINT8)Value, 0);
  Value \>>= 8;
  mOpcodeHandler.AddByte ((UINT8)Value, 0);
  Value \>>= 8;
  mOpcodeHandler.AddByte ((UINT8)Value, 0);
}
VOID 
WriteOpByte (
  UINT32    LineNum,
  UINT8     ByteValue
  )
/*++

Routine Description:
  
  During parsing, we came upon a new VFR opcode. At this point we flush
  the output queue and then queue up this byte (with 'O' for opcode tag).

Arguments:

  ByteValue   - opcode value

Returns:

  None.

--*/
{
  mOpcodeHandler.AddOpcodeByte (ByteValue, LineNum);
}
VOID 
WriteByte (
  UINT8   ByteValue, 
  UINT8   Key
  )
/*++

Routine Description:
  
  During parsing of the VFR we spoonfeed this function with bytes to write to
  the output VFR binary file. This function simply queues up the bytes, and
  the queue gets flushed each time a new VFR opcode is encountered.

Arguments:

  ByteValue   - raw byte to write
  Key         - character to tag the byte with when we write ByteValue to the
                output file.

Returns:

  None.

--*/
{
  mOpcodeHandler.AddByte (ByteValue, Key);
}
VOID 
WriteWord (
  UINT32  Value
  )
/*++

Routine Description:
  During VFR parsing we came upon a case where we need to write out a 
  16-bit value. Queue it up.

Arguments:
  Value - value to write.

Returns:
  None.

--*/
{
  mOpcodeHandler.AddByte ((UINT8)Value, 0);
  mOpcodeHandler.AddByte ((UINT8)((Value \>> 8) & 0xFF), 0);
}
VOID 
WriteStringIdWord (
  UINT16 WordValue
  )
{
  mOpcodeHandler.AddByte ((UINT8)WordValue, 'S');
  mOpcodeHandler.AddByte ((UINT8)((WordValue \>> 8) & 0xFF), 0);
}
VOID
FreeGotoReferences ()
/*++

Routine Description:
  Called during cleanup to free up the memory we allocated when
  keeping track of VFR goto statements.

Arguments:
  None

Returns:
  None

--*/
{
  GOTO_REFERENCE  *CurrRef;
  GOTO_REFERENCE  *NextRef;
  FORM_ID_VALUE   *CurrFormId;
  FORM_ID_VALUE   *NextFormId;
  UINT8           Found;
  INT8            Name[20];

  //
  // Go through all the "goto" references and make sure there was a 
  // form ID of that value defined.
  //
  for (CurrRef = mGotoReferences; CurrRef != NULL; CurrRef = CurrRef->Next) {
    Found = 0;
    for (CurrFormId = mFormIdValues; CurrFormId != NULL; CurrFormId = CurrFormId->Next) {
      if (CurrRef->Value == CurrFormId->Value) {
        Found = 1;
        break;
      }
    }
    if (!Found) {
      sprintf (Name, "%d", (UINT32)CurrRef->Value);
      PrintErrorMessage (CurrRef->RefLineNum, Name, "undefined form ID");
    }  
  }  
  //
  // Now free up the form id and goto references
  //
  CurrFormId = mFormIdValues;
  while (CurrFormId != NULL) {
    NextFormId = CurrFormId->Next;
    free (CurrFormId);
    CurrFormId = NextFormId;
  }
  mFormIdValues = NULL;
  CurrRef = mGotoReferences;
  while (CurrRef != NULL) {
    NextRef = CurrRef->Next;
    free (CurrRef);
    CurrRef = NextRef;
  }  
  mGotoReferences = NULL;
}
VOID
AddGotoReference (
  UINT32  GotoNumber,
  UINT32  LineNum
  )
/*++

Routine Description:
  During VFR parsing we came upon a goto statement. Since we support
  forward references, save the referenced label and at the end of parsing
  we'll check that the label was actually defined somewhere.

Arguments:
  GotoNumber  - the label number referenced
  LineNum     - the line number where the reference was made (used for
                error reporting)

Returns:
  None

--*/
{
  GOTO_REFERENCE *NewRef;
  
  NewRef = (GOTO_REFERENCE *)malloc (sizeof (GOTO_REFERENCE));
  if (NewRef == NULL) {
    Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");
    return;
  }
  memset ((char *)NewRef, 0, sizeof (GOTO_REFERENCE));
  NewRef->Value = (UINT16)GotoNumber;
  NewRef->RefLineNum = LineNum;
  NewRef->Next = mGotoReferences;
  mGotoReferences = NewRef;
}
VOID
AddFormId (
  INT32   FormIdValue,
  UINT32  LineNum
  )
/*++

Routine Description:
  This function is called when we parse "form formid = 3" statements.
  We save the form ID valud so we can verify that duplicates are not
  defined. Also, these are the targets of goto statements, so when we're
  done parsing the script we also go through all the goto statements to
  check that there was a target FormId defined as referenced by each
  goto statement.
  
  Note that formid = 0 is invalid.

Arguments:
  FormIdValue  - the parsed value for the Form ID
  LineNum      - line number of the source file we're parsing

Returns:
  NA

--*/
{
  FORM_ID_VALUE *NewFormId;
  char          *FileName;
  char          *FileName2;
  UINT32        LineNum2;  
  //
  // Verify that FormId != 0
  //
  if (FormIdValue == 0) {
    FileName = ConvertLineNumber (&LineNum);
    Error (FileName, LineNum, 0, "form ID cannot be 0", NULL);
    return;
  }
  //
  // First go through all previously defined form IDs and make sure they have not defined
  // duplicates.
  //
  for (NewFormId = mFormIdValues; NewFormId != NULL; NewFormId = NewFormId->Next) {
    if ((UINT16)FormIdValue == NewFormId->Value) {
      FileName = ConvertLineNumber (&LineNum);
      LineNum2 = NewFormId->LineNum;
      FileName2 = ConvertLineNumber (&LineNum2);
      Error (FileName, LineNum, 0, NULL, "form ID %d already defined", FormIdValue);
      Error (FileName2, LineNum2, 0, NULL, "form ID %d previous definition", FormIdValue);
      return;
    }
  }
  //
  // Allocate memory for a new one 
  //
  NewFormId = (FORM_ID_VALUE *)malloc (sizeof (FORM_ID_VALUE));
  if (NewFormId == NULL) {
    Error (PROGRAM_NAME, 0, 0, NULL, "memory allocation failure");
    return;
  }
  memset ((char *)NewFormId, 0, sizeof (FORM_ID_VALUE));
  NewFormId->LineNum = LineNum;
  NewFormId->Next = mFormIdValues;
  NewFormId->Value = (UINT16)FormIdValue;
  mFormIdValues = NewFormId;
}
UINT32
GetNumber (
  INT8    *NumStr,
  UINT32  LineNum,
  UINT32  NumBytes
  )
{
  UINT32 Value;
  
  if ((NumStr[0] == '0') && (NumStr[1] == 'x')) {
    AtoX (NumStr + 2, 4, &Value);
  } else {
    Value = (UINT32)atoi (NumStr);
  }
  //
  // Check range
  //
  if ((NumBytes < 4) && (Value & ((UINT32)0xFFFFFFFF << (NumBytes * 8)))) {
    PrintErrorMessage (LineNum, NumStr, "value out of range");
    return 0;
  }
  return Value;
}

>>

} // end grammar class

